package com.bestcem.xm.user.service.impl;

import cn.hutool.core.lang.ObjectId;
import cn.hutool.crypto.digest.DigestUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.bestcem.xm.bi.grpc.v1.services.ShareRequestPwdFlag;
import com.bestcem.xm.bi.grpc.v1.services.ShareShareType;
import com.bestcem.xm.bi.grpc.v1.services.ShareStatus;
import com.bestcem.xm.common.core.constant.RegTypes;
import com.bestcem.xm.common.core.domain.web.ServiceResult;
import com.bestcem.xm.common.core.enums.AuthErrorCodeEnum;
import com.bestcem.xm.common.core.enums.ReqErrorCodeEnum;
import com.bestcem.xm.common.core.enums.ResourceErrorCodeEnum;
import com.bestcem.xm.common.core.enums.ValidationErrorCodeEnum;
import com.bestcem.xm.common.core.uitls.DateUtil;
import com.bestcem.xm.common.core.uitls.EmailUtil;
import com.bestcem.xm.componet.redis.service.RedisService;
import com.bestcem.xm.user.config.properties.MailSubjectNotifyTitleEnumProperties;
import com.bestcem.xm.user.config.properties.SettingProperties;
import com.bestcem.xm.user.config.properties.SingleOrgProperties;
import com.bestcem.xm.user.constant.UserCacheKeyConstant;
import com.bestcem.xm.user.entity.pojo.UserDO;
import com.bestcem.xm.user.enums.*;
import com.bestcem.xm.user.grpc.client.*;
import com.bestcem.xm.user.grpc.client.dto.*;
import com.bestcem.xm.user.grpc.client.dto.redis.WxCacheDTO;
import com.bestcem.xm.user.service.*;
import com.bestcem.xm.user.service.dto.redis.FastCreateUserDTO;
import com.bestcem.xm.user.service.dto.redis.UserLoginSessionDTO;
import com.bestcem.xm.user.service.dto.redis.UserRegisterDTO;
import com.bestcem.xm.user.service.dto.third.WenJuanUserInfoDTO;
import com.bestcem.xm.user.service.dto.user.*;
import com.bestcem.xm.user.service.third.*;
import com.bestcem.xm.user.util.business.*;
import com.bestcem.xm.user.util.business.captcha.CaptchaUtil;
import com.bestcem.xm.user.util.convert.UserLoginConvert;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.text.MessageFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 登录注册服务
 *
 * @author wangyunyun <yunyun.wang@idiaoyan.com>
 * @version v1.0
 * @date 2022/3/1 14:30
 */
@Slf4j
@Service
public class UserAuthServiceImpl implements UserAuthService {

    @Resource
    private CaptchaUtil captchaUtil;

    @Resource
    private UserCacheUtil userCacheUtil;

    @Resource
    private SettingProperties settingProperties;

    @Resource
    private SingleOrgProperties singleOrgProperties;

    @Resource
    private UserLoginConvert userLoginConvert;

    @Autowired
    private IpGrpcService ipGrpcClientService;

    @Resource
    private UserJwtTokenUtil userJwtTokenUtil;

    @Resource
    private OrganizationService organizationService;

    @Autowired
    private UserService userService;

    @Autowired
    private RedisService redisService;

    @Autowired
    private BaiduOcpcService baiduOcpcService;

    @Autowired
    private GrowingIOService growingIOService;

    @Resource
    private WenJuanService wenJuanService;

    @Autowired
    private LuosimaoService luosimaoService;

    @Autowired
    private UserOrgBusinessService userOrgBusinessService;

    @Autowired
    private IndustryGrpcService industryGrpcService;

    @Autowired
    private PackageGrpcService packageGrpcService;

    @Autowired
    private UserBusinessService userBusinessService;

    @Autowired
    private UserPrivateKeyBusinessService userPrivateKeyBusinessService;

    @Autowired
    private PhoneService phoneService;

    @Autowired
    private UserBaseBiGrpcService userBaseBiGrpcService;

    @Autowired
    private UserBaseQdesGrpcService userBaseQdesGrpcService;

    @Resource
    private UserBookingTrackService userBookingTrackService;

    @Autowired
    private UserWxService userWxService;

    @Autowired
    private UserUtil userUtil;

    @Autowired
    private BusinessCacheUtil businessCacheUtil;

    @Autowired
    private MailService mailService;

    @Autowired
    private UserMfaSettingService userMfaSettingService;

    @Resource
    private UserScenarioService userScenarioService;

    @Resource
    private UserWechatGrpcService userWechatGrpcService;


    @Override
    public ServiceResult<UserLoginDTO> orgRegister(OrgRegisterDTO orgRegisterDTO) {
        // 校验参数
        if (orgRegisterDTO.getHasAcceptPolicy() == null || !orgRegisterDTO.getHasAcceptPolicy()) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "policy");
        }
        if (StringUtils.isBlank(orgRegisterDTO.getPassword()) || !PwdUtil.checkPassword(orgRegisterDTO.getPassword()).isSuccess()) {
            return ServiceResult.fail(ValidationErrorCodeEnum.PWD_PATTERN_ERROR, "密码格式有误, 请重新输入");
        }
        if (StringUtils.isBlank(orgRegisterDTO.getUserName())) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "userName is empty.");
        }
        if (StringUtils.isBlank(orgRegisterDTO.getIndustryCode())) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "industry_code is lost.");
        }
        if (StringUtils.isBlank(orgRegisterDTO.getSessionId())) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "session_id is empty.");
        }
        // 校验邮箱
        if (StringUtils.isBlank(orgRegisterDTO.getEmail())) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "email is empty.");
        }
        orgRegisterDTO.setEmail(StringUtils.strip(orgRegisterDTO.getEmail()));
        if (Boolean.FALSE.equals(EmailUtil.validateEmail(orgRegisterDTO.getEmail()))) {
            return ServiceResult.fail(ValidationErrorCodeEnum.EMAIL_ERROR, "邮箱有误,请重新输入");
        }
        // 校验场景
        if (StringUtils.isBlank(orgRegisterDTO.getScenarioId())) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "scenario_id is empty.");
        }
        ServiceResult<UserScenarioDTO> scenarioResult = userScenarioService.getUserScenarioById(orgRegisterDTO.getScenarioId());
        if (Objects.isNull(scenarioResult.getData())) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "scenario_id is empty.");
        }

        // 获取行业以及对应区域的销售的邮箱
        IndustryDTO industryDTO = this.getIndustryAndSalesEmails(orgRegisterDTO.getIpAddress(), orgRegisterDTO.getIndustryCode());
        if (industryDTO == null || Objects.isNull(industryDTO.getCode())) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "industry_code is lost.");
        }

        // 从缓存中获取信息
        UserRegisterDTO userRegisterDTO = businessCacheUtil.getWxUserCacheInfo(orgRegisterDTO.getSessionId());
        if (userRegisterDTO == null || StringUtils.isBlank(userRegisterDTO.getPhone())) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "session_id is lost.");
        }

        // 校验注册手机号是否已经存在
        ServiceResult<String> checkPhoneRt = checkRegisterPhone(userRegisterDTO.getPhone(), UserEditionEnum.FREE.getType());
        if (!checkPhoneRt.isSuccess()) {
            return ServiceResult.fail(null, checkPhoneRt.getMsg(), checkPhoneRt.getErrorCode());
        }
        // 添加来源
        String source = this.buildSource(orgRegisterDTO);
        orgRegisterDTO.setSource(source);
        // 设置公司和用户信息
        ServiceResult<JSONObject> registerOrgRt = userOrgBusinessService.registerOrg(orgRegisterDTO, userRegisterDTO, industryDTO);
        if (!registerOrgRt.isSuccess()) {
            return ServiceResult.fail(null, registerOrgRt.getMsg(), registerOrgRt.getErrorCode());
        }

        // 获取公司和用户
        UserDTO userDTO = registerOrgRt.getData().getObject(UserDTO.class.getName(), UserDTO.class);
        OrganizationDTO organizationDTO = registerOrgRt.getData().getObject(OrganizationDTO.class.getName(), OrganizationDTO.class);
        // 获取类型为基础试用版的套餐信息
        PackageDTO freeTrialPackageDTO = registerOrgRt.getData().getObject(PackageDTO.class.getName(), PackageDTO.class);

        // 发送免费公司创建短信给用户 notify_free_org_created
        userWxService.notifyFreeOrgCreated(organizationDTO.getCode(), userDTO.getUserName(), organizationDTO.getName(),
                null, organizationDTO.getPackageExpireTime(), userDTO.getPhone(), organizationDTO.getOrgId());

        // 判断是否是内部用户,不是的话,发送免费公司创建通知给销售
        if (UserInnerFlagEnum.OUTTER.getType().equals(userDTO.getInnerFlag())) {
            if (log.isInfoEnabled()) {
                log.info("[USER] 发送非内部用户创建销售邮件通知 industryDTO:{}", industryDTO);
            }
            this.notifyApplicationByEmailByRegister(orgRegisterDTO, userRegisterDTO, industryDTO, organizationDTO, scenarioResult.getData());
        }

        // 设置埋点信息
        this.createBookingTrack(orgRegisterDTO, userDTO, organizationDTO);
        // 异步回调百度回传转化数据
        baiduOcpcService.sendConvertData(orgRegisterDTO.getNewType(), orgRegisterDTO.getLogidUrl());
        // 删除session_id缓存
        businessCacheUtil.delWxUserCacheInfo(orgRegisterDTO.getSessionId());

        // 创建用户登录日志
        userBusinessService.updateUserLastLoginTime(userDTO, orgRegisterDTO.getIpAddress());

        // 设置token
        AuthDTO authDTO = AuthDTO.builder()
                .userId(userDTO.getUserId())
                .realName(userDTO.getName())
                .avatar(userDTO.getAvatar())
                .superRole(NumberUtils.INTEGER_ONE)
                .orgId(organizationDTO.getOrgId())
                .orgCode(organizationDTO.getCode())
                .packageExpireTime(UserJwtTokenUtil.formatDate(organizationDTO.getPackageExpireTime()))
                .build();

        // 设置返回值
        UserLoginDTO userLoginDTO = this.buildLoginResult(authDTO, userDTO, organizationDTO, freeTrialPackageDTO.getName());
        return ServiceResult.success(userLoginDTO);
    }

    /**
     * 发送免费公司创建通知给销售
     *
     * @param orgRegisterDTO
     * @param userRegisterDTO
     * @param industryDTO
     * @param organizationDTO
     * @param scenarioDTO
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/7/14 20:45
     */
    private void notifyApplicationByEmailByRegister(OrgRegisterDTO orgRegisterDTO, UserRegisterDTO userRegisterDTO, IndustryDTO industryDTO, OrganizationDTO organizationDTO, UserScenarioDTO scenarioDTO) {
        NotifyApplicationByEmailDTO emailDTO = new NotifyApplicationByEmailDTO();
        emailDTO.setToEmail(industryDTO.getSalesEmails());
        emailDTO.setUsername(orgRegisterDTO.getUserName());
        emailDTO.setCompanyName(orgRegisterDTO.getOrgName());
        emailDTO.setCompanyType(industryDTO.getName());
        emailDTO.setEmail(orgRegisterDTO.getEmail());
        emailDTO.setPhone(userRegisterDTO.getPhone());
        emailDTO.setContent("无");
        emailDTO.setSubject(String.format("[%s申请信息-免费版]", settingProperties.getXmName()));
        // 查询手机号归属地
        emailDTO.setLoc(phoneService.getPhoneLocation(userRegisterDTO.getPhone()));
        // 设置来源
        emailDTO.setSource(this.buildSource(orgRegisterDTO));
        emailDTO.setOrgId(organizationDTO.getOrgId());
        emailDTO.setScenarioDTO(scenarioDTO);
        userUtil.notifyApplicationByEmail(emailDTO);
    }

    /**
     * 发送免费公司创建通知给销售-设置来源
     *
     * @param orgRegisterDTO
     * @return String
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/7/15 10:40
     */
    private String buildSource(OrgRegisterDTO orgRegisterDTO) {
        if (StringUtils.isBlank(orgRegisterDTO.getUtmSource()) && StringUtils.isBlank(orgRegisterDTO.getPlan())) {
            return StringUtils.EMPTY;
        }

        if (StringUtils.isNotBlank(orgRegisterDTO.getUtmSource())) {
            return StringUtils.isNotBlank(orgRegisterDTO.getPlan())
                    ? String.format("?utm_source=%s&plan=%s", orgRegisterDTO.getUtmSource(), orgRegisterDTO.getPlan())
                    : String.format("?utm_source=%s", orgRegisterDTO.getUtmSource());
        } else {
            return String.format("?plan=%s", orgRegisterDTO.getPlan());
        }
    }

    /**
     * 设置埋点信息
     *
     * @param orgRegisterDTO  注册对象
     * @param userDTO         用户
     * @param organizationDTO 公司
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/4/29 09:23
     */
    private void createBookingTrack(OrgRegisterDTO orgRegisterDTO, UserDTO userDTO, OrganizationDTO organizationDTO) {
        // 判断设备id是否不为空, 不为空的话设置埋点信息
        if (StringUtils.isNotBlank(orgRegisterDTO.getTraceId())) {
            // 通过traceId查询埋点信息,如果存在的话则更新,不存在则新增
            UserBookingTrackDTO bookingTrackDTO = new UserBookingTrackDTO();
            bookingTrackDTO.setTraceId(orgRegisterDTO.getTraceId());
            List<UserBookingTrackDTO> trackDTOList = userBookingTrackService.selectBySelective(bookingTrackDTO).getData();

            // 设置要保存的埋点参数
            bookingTrackDTO.setOrgCode(organizationDTO.getCode());
            bookingTrackDTO.setPlatform(UserBookingTrackPlatformEnum.FREE_WEB.getType());
            bookingTrackDTO.setClickFlag(UserEnum.USER_YES.getFlag());
            bookingTrackDTO.setUtmSource(orgRegisterDTO.getUtmSource());
            bookingTrackDTO.setPlan(orgRegisterDTO.getPlan());
            bookingTrackDTO.setDevice(orgRegisterDTO.getDevice());
            bookingTrackDTO.setOperatorId(userDTO.getCreatorId());

            if (CollectionUtils.isEmpty(trackDTOList)) {
                // 埋点信息不存在 新增
                bookingTrackDTO.setCreatorId(userDTO.getCreatorId());
                userBookingTrackService.insertSelective(bookingTrackDTO);
            } else {
                // 埋点信息存在 更新
                bookingTrackDTO.setBookingtrackId(trackDTOList.get(0).getBookingtrackId());
                userBookingTrackService.updateSelectiveById(bookingTrackDTO);
            }
        }
    }

    /**
     * 通过行业编码和ip获取行业信息和行业对应的地区的销售邮箱
     *
     * @param ipAddress    ip地址
     * @param industryCode 行业code
     * @return
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/7 17:50
     */
    @Override
    public IndustryDTO getIndustryAndSalesEmails(String ipAddress, String industryCode) {
        // 通过ip所在省+所属行业编码 获取行业以及销售通知邮箱
        return industryGrpcService.getByCodeAndDistrict(industryCode, null);
    }

    /**
     * 通过行业id和ip获取行业信息和行业对应的地区的销售电话
     *
     * @param ipAddress  ip地址
     * @param industryId 行业id
     * @return
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/7 17:50
     */
    @Override
    public IndustryDTO getIndustryAndContacts(String ipAddress, String industryId) {
        // 获取ip所在省的地区
        IpInfoDTO ipInfo = ipGrpcClientService.getIpInfo(ipAddress);
        String district = IpDistrictEnum.getDistrictByProvince(ipInfo != null ? ipInfo.getProvince() : null, true);

        // 通过ip所在省+所属行业id 获取行业以及销售通知邮箱
        return industryGrpcService.getByIdAndDistrict(industryId, district);
    }

    /**
     * 设置用户登录返回值
     *
     * @param authDTO         用户认证dto
     * @param userDTO         用户dto
     * @param organizationDTO 公司dto
     * @param packageName     套餐名称
     * @return UserLoginDTO
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/4 11:52
     */
    private UserLoginDTO buildLoginResult(AuthDTO authDTO, UserDTO userDTO, OrganizationDTO organizationDTO, String packageName) {
        // 生成token和refreshToken
        String token = userJwtTokenUtil.generateToken(authDTO);
        String refreshToken = userJwtTokenUtil.generateRefreshToken(authDTO.getOrgCode(), authDTO.getUserId());

        // 设置growingio信息
        GrowingioDTO growingioDTO = growingIOService.getUserProperty(userDTO, organizationDTO, packageName);

        // 设置返回值
        UserLoginDTO userLoginDTO = new UserLoginDTO();
        userLoginDTO.setOrgCode(organizationDTO.getCode());
        userLoginDTO.setOrgId(organizationDTO.getOrgId());
        userLoginDTO.setGrowingio(growingioDTO);
        userLoginDTO.setToken(token);
        userLoginDTO.setRefreshToken(refreshToken);
        return userLoginDTO;
    }

    @Override
    public ServiceResult<RefreshTokenDTO> getToken(String userName, String password, boolean encrypt, String orgCode) {
        // python引用：apps_mysql.auth.handlers.AuthTokenViewHandler.post
        // 参数校验
        if (StringUtils.isBlank(userName) || StringUtils.isBlank(password) || StringUtils.isBlank(orgCode)) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "userName|password|orgCode都不能为空");
        }

        AuthDTO.AuthDTOBuilder builder = AuthDTO.builder();

        // 校验公司套餐并设置相关token信息
        ServiceResult<OrganizationDTO> orgInfoData = organizationService.selectByCodeAndStatus(orgCode, OrganizationStatusEnum.ACTIVATED.getStatus());
        ServiceResult<AuthDTO.AuthDTOBuilder> checkPackResult = checkPackAndHandlerToken(orgInfoData, builder);
        if (!checkPackResult.isSuccess()) {
            return ServiceResult.fail(checkPackResult.getErrorData(), checkPackResult.getMsg());
        }
        builder = checkPackResult.getData();
        OrganizationDTO orgInfo = orgInfoData.getData();
        String orgId = orgInfo.getOrgId();

        // 校验用户信息
        UserDTO userParam = new UserDTO();
        userParam.setOrgId(orgId);
        userParam.setStatus(UserStatusEnum.ACTIVATED.getStatus());
        if (userName.contains("@")) {
            userParam.setEmail(userName.toLowerCase());
        } else {
            userParam.setUserName(userName);
        }
        ServiceResult<List<UserDO>> userResult = userService.selectByOrgIdSelective(userParam);
        if (!userResult.isSuccess() || CollectionUtils.isEmpty(userResult.getData())) {
            return ServiceResult.fail(AuthErrorCodeEnum.USER_NAME_NOT_EXISTS, "user not exist.");
        }
        UserDO userInfo = userResult.getData().get(0);

        // 校验密码
        if (encrypt) {
            // 密码解密
            ServiceResult<String> decryptPasswordResult = userPrivateKeyBusinessService.decryptPassword(orgId, password);
            if (!decryptPasswordResult.isSuccess()) {
                return ServiceResult.fail(decryptPasswordResult.getErrorData(), decryptPasswordResult.getMsg());
            }
            password = decryptPasswordResult.getData();
        }
        boolean checkPassword = PwdUtil.checkPassword(password, userInfo.getPassword(), userInfo.getSalt());
        if (!checkPassword) {
            return ServiceResult.fail(AuthErrorCodeEnum.PWD_ERROR, "user_name or password not true.");
        }

        builder.userId(userInfo.getUserId());
        builder.realName(userInfo.getName());
        builder.avatar(userInfo.getAvatar());

        // 设置用户是否是系统管理员和高级管理员
        builder.superRole(userBusinessService.checkSuper(userInfo.getUserId(), userInfo.getOrgId()).getData());
        builder.senior(userBusinessService.checkSenior(userInfo.getUserId(), userInfo.getOrgId()).getData());

        // 生成jwt
        String token = userJwtTokenUtil.generateToken(builder.build());
        String refreshToken = userJwtTokenUtil.generateRefreshToken(orgCode, userInfo.getUserId());

        // 更新最后登录时间
        updateUserLastLoginTime(userInfo.getUserId());
        return ServiceResult.success(new RefreshTokenDTO(token, refreshToken));
    }

    @Override
    public ServiceResult<RefreshTokenDTO> refreshToken(String userId, String orgCode) {
        // 参数校验
        if (StringUtils.isBlank(userId)) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "userId不能为空");
        }

        AuthDTO.AuthDTOBuilder builder = AuthDTO.builder();
        String orgId = "";

        // 校验公司信息
        if (StringUtils.isNotBlank(orgCode)) {
            // 根据公司code查询公司信息
            ServiceResult<OrganizationDTO> orgInfo = organizationService.selectByCodeAndStatus(orgCode, OrganizationStatusEnum.ACTIVATED.getStatus());
            // 校验套餐并设置相关token信息
            ServiceResult<AuthDTO.AuthDTOBuilder> checkPackResult = checkPackAndHandlerToken(orgInfo, builder);
            if (!checkPackResult.isSuccess()) {
                return ServiceResult.fail(checkPackResult.getErrorData(), checkPackResult.getMsg());
            }
            orgId = orgInfo.getData().getOrgId();
            builder = checkPackResult.getData();
        }

        // 校验用户信息
        ServiceResult<UserDTO> userResult = userService.selectById(userId);
        if (!userResult.isSuccess() || Objects.isNull(userResult.getData())) {
            return ServiceResult.fail(AuthErrorCodeEnum.USER_NOT_EXISTS, "user be disabled.");
        }
        UserDTO userInfo = userResult.getData();
        if (!orgId.equals(userInfo.getOrgId())) {
            return ServiceResult.fail(AuthErrorCodeEnum.PWD_ERROR, "user is inconsistent with the organization.");
        }
        builder.userId(userInfo.getUserId());
        builder.realName(userInfo.getName());
        builder.avatar(userInfo.getAvatar());

        // 设置用户是否是系统管理员和高级管理员
        builder.superRole(userBusinessService.checkSuper(userInfo.getUserId(), userInfo.getOrgId()).getData());
        builder.senior(userBusinessService.checkSenior(userInfo.getUserId(), userInfo.getOrgId()).getData());

        // 生成jwt
        String token = userJwtTokenUtil.generateToken(builder.build());
        String refreshToken = userJwtTokenUtil.generateRefreshToken(orgCode, userId);
        return ServiceResult.success(new RefreshTokenDTO(token, refreshToken));
    }

    @Override
    public ServiceResult<Boolean> checkPassword(String userId, String password) {
        if (StringUtils.isBlank(userId) || StringUtils.isBlank(password)) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "用户id和密码都不能为空");
        }
        ServiceResult<UserDTO> userDTOServiceResult = userService.selectById(userId);
        if (!userDTOServiceResult.isSuccess() || Objects.isNull(userDTOServiceResult.getData())) {
            return ServiceResult.fail(ResourceErrorCodeEnum.NOT_FOUND, "未查询对应的用户信息");
        }
        UserDTO user = userDTOServiceResult.getData();
        return ServiceResult.success(PwdUtil.checkPassword(password, user.getPassword(), user.getSalt()));
    }

    @Override
    public ServiceResult<Boolean> updatePassword(String userId, String oldPassword, String newPassword) {
        // 校验用户的旧密码是否正确
        ServiceResult<Boolean> checkPassword = checkPassword(userId, oldPassword);
        if (!checkPassword.isSuccess() || !Boolean.TRUE.equals(checkPassword.getData())) {
            return ServiceResult.fail(AuthErrorCodeEnum.PWD_ERROR, "密码错误");
        }
        ServiceResult<Boolean> resetResult = resetPassword(userId, newPassword);
        if (resetResult.isSuccess()) {
            // 更新用户token版本号
            userJwtTokenUtil.updateTokenVersion(userId);
        }
        return resetResult;
    }

    @Override
    public ServiceResult<Boolean> resetPassword(String userId, String password) {
        // python引用：apps_mysql.user.util.utils.reset_password
        /*
         * @params user_id: 用户id
         * @params password: 用户新密码(明文)
         */
        // 新密码是否是重置的，如果是重置需要发短信
        String autoPwd = "";
        // 重置密码
        if (StringUtils.isEmpty(password)) {
            // 随机生成密码
            password = PwdUtil.genPassword();
            autoPwd = password;
        } else {
            // 校验密码复杂度
            ServiceResult<String> check = PwdUtil.checkPassword(password);
            if (!check.isSuccess()) {
                return ServiceResult.fail(ValidationErrorCodeEnum.SIMPLE_PASSWD, password + "is too simple");
            }
        }
        String generatorSalt = PwdUtil.genSalt();
        // 更新用户密码
        password = PwdUtil.getEncodePwd(password, generatorSalt);
        ServiceResult<Boolean> updateResult = userService.updateUserPassword(userId, password, generatorSalt);
        if (Boolean.TRUE.equals(updateResult.getData()) && StringUtils.isNotBlank(autoPwd)) {
            ServiceResult<UserDTO> userInfoResult = userService.selectById(userId);
            UserDTO userInfo = userInfoResult.getData();
            if (Objects.nonNull(userInfo) && StringUtils.isNotBlank(userInfo.getEmail())) {
                String content = "您的密码重置为 " + autoPwd;
                mailService.sendEmail(new String[]{userInfo.getEmail()},
                        MailSubjectNotifyTitleEnumProperties.NotifyTitleEnum.OM_USER_RESET_PASSWORD.getKey(), content);
            }
        }
        return updateResult;
    }

    @Override
    public ServiceResult<String> validatePhone(String phone) {
        // 校验参数
        if (StringUtils.isBlank(phone)) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "请输入手机号");
        }
        if (!Pattern.compile(RegTypes.MOBILE).matcher(phone).matches()) {
            return ServiceResult.fail(ValidationErrorCodeEnum.PHONE_ERROR, "手机号有误,请重新输入");
        }

        // 校验手机号是否已经存在
        ServiceResult<String> checkPhoneRt = checkRegisterPhone(phone, null);
        if (!checkPhoneRt.isSuccess()) {
            ServiceResult<String> failResult = ServiceResult.fail(checkPhoneRt.getMsg());
            failResult.setErrorCode(checkPhoneRt.getErrorCode());
            return failResult;
        }

        return ServiceResult.success();
    }

    @Override
    public ServiceResult<String> validatePhoneAndCode(String phone, String vcode) {
        // 校验参数
        if (StringUtils.isBlank(phone)) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "请输入手机号");
        }
        if (!Pattern.compile(RegTypes.MOBILE).matcher(phone).matches()) {
            return ServiceResult.fail(ValidationErrorCodeEnum.PHONE_ERROR, "手机号有误,请重新输入");
        }
        if (StringUtils.isBlank(vcode)) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "请输入短信验证码");
        }
        // 校验验证码
        String cacheVcode = userCacheUtil.get(UserCacheKeyConstant.FREE_REGISTER_PHONE_VCODE, phone);
        if (StringUtils.isBlank(cacheVcode) || !cacheVcode.equalsIgnoreCase(vcode)) {
            return ServiceResult.fail(ValidationErrorCodeEnum.VCODE_ERROR, "短信验证码错误");
        }

        // 校验注册手机号是否已经存在
        ServiceResult<String> checkPhoneRt = checkRegisterPhone(phone, UserEditionEnum.FREE.getType());
        if (!checkPhoneRt.isSuccess()) {
            return checkPhoneRt;
        }

        // 添加加缓存-注册session信息, 并返回结果
        UserRegisterDTO userRegisterDTO = new UserRegisterDTO();
        userRegisterDTO.setPhone(phone);
        return ServiceResult.success(businessCacheUtil.cacheWxUserInfo(userRegisterDTO));
    }

    @Override
    public ServiceResult<UserLoginDTO> fastLogin(String phone, String vcode, String ipAddress) {
        // 校验参数
        if (StringUtils.isBlank(phone)) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "请输入手机号");
        }
        if (!Pattern.compile(RegTypes.MOBILE).matcher(phone).matches()) {
            return ServiceResult.fail(ValidationErrorCodeEnum.PHONE_ERROR, "手机号有误,请重新输入");
        }
        if (StringUtils.isBlank(vcode)) {
            return ServiceResult.fail("请输入短信验证码");
        }
        // 校验验证码
        String cacheVcode = userCacheUtil.get(UserCacheKeyConstant.USER_FAST_LOGIN_PHONE_VCODE, phone);
        if (StringUtils.isBlank(cacheVcode) || !cacheVcode.equalsIgnoreCase(vcode)) {
            return ServiceResult.fail(ValidationErrorCodeEnum.VCODE_ERROR, "短信验证码错误");
        }

        // 判断手机号是否属于测试手机号白名单, 如果是的话, 提示注册
        if (settingProperties.getTestWhitePhoneList().contains(phone)) {
            return this.mobileFastRegisterResult(phone);
        }

        // 通过手机号查询激活中的用户
        UserDTO userQuery = new UserDTO();
        userQuery.setPhone(phone);
        userQuery.setStatus(UserStatusEnum.ACTIVATED.getStatus());
        List<UserDTO> existUsers = userService.listByCondition(userQuery).getData();

        // 判断激活中的用户数量
        if (CollectionUtils.isEmpty(existUsers)) {
            // 用户数量等于0: 提示注册
            return this.mobileFastRegisterResult(phone);
        } else if (existUsers.size() == 1) {
            // 用户数量等于1: 登录
            // 查询并校验公司信息
            OrganizationDTO organizationDTO = organizationService
                    .selectByIdAndStatus(existUsers.get(0).getOrgId(), OrganizationStatusEnum.ACTIVATED.getStatus()).getData();
            if (organizationDTO == null) {
                return ServiceResult.fail(ResourceErrorCodeEnum.NOT_FOUND,
                        String.format("Organization[id=%s] not found", existUsers.get(0).getOrgId()));
            }
            return this.mobileFastLoginResult(existUsers.get(0), organizationDTO, ipAddress);
        } else {
            // 用户数量大于1: 多用户登录处理
            return this.mobileFastMultLoginResult(existUsers, ipAddress);
        }
    }

    @Override
    public ServiceResult<UserLoginDTO> fastLoginByOrg(String orgCode, String sessionId, String ipAddress) {
        // 校验参数
        if (StringUtils.isBlank(sessionId)) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "session_id lost");
        }
        if (StringUtils.isBlank(orgCode)) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "org_code lost");
        }

        // 查询登录sessionId缓存
        UserLoginSessionDTO loginSessionDTO = businessCacheUtil.getFastLoginUser(sessionId);
        if (loginSessionDTO == null) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, String.format("session %s not found", sessionId));
        }

        // 查询并校验公司
        OrganizationDTO organizationDTO = organizationService.selectByCodeAndStatus(orgCode, OrganizationStatusEnum.ACTIVATED.getStatus()).getData();
        if (organizationDTO == null) {
            return ServiceResult.fail(AuthErrorCodeEnum.ORG_NOT_EXISTS, "org not find");
        }

        // 查询并校验用户
        UserDTO userDTO = new UserDTO();
        userDTO.setPhone(loginSessionDTO.getPhone());
        userDTO.setOrgId(organizationDTO.getOrgId());
        userDTO.setStatus(UserStatusEnum.ACTIVATED.getStatus());
        List<UserDTO> existUsers = userService.listByCondition(userDTO).getData();
        UserDTO existUser = existUsers.get(0);
        // 判断是否需要更新用户的微信小程序openid
        if (StringUtils.isNotBlank(loginSessionDTO.getWxMiniOpenid())
                && !loginSessionDTO.getWxMiniOpenid().equals(existUser.getWxMiniOpenid())) {
            // 需要更新用户的微信小程序openid
            UserDTO upadteUser = new UserDTO();
            upadteUser.setUserId(existUser.getUserId());
            upadteUser.setWxMiniOpenid(loginSessionDTO.getWxMiniOpenid());
            userService.updateById(upadteUser);
        }

        return this.mobileFastLoginResult(existUser, organizationDTO, ipAddress);
    }

    @Override
    public ServiceResult<UserLoginDTO> phoneOrEmailFastLogin(HttpServletResponse response, String username,
                                                             String password, String sessionId, String ipAddress) {
        // 校验参数
        if (StringUtils.isBlank(username)) {
            return ServiceResult.fail(ValidationErrorCodeEnum.USER_NAME_EMPTY, "user_name is empty.");
        }
        if (StringUtils.isBlank(password)) {
            return ServiceResult.fail(ValidationErrorCodeEnum.PWD_EMPTY, "password is empty.");
        }

        // 根据手机号或者邮箱查询用户
        List<UserDTO> existUsers = userBusinessService.listUserByPhoneOrEmail(username, false, null).getData();
        if (CollectionUtils.isEmpty(existUsers)) {
            return ServiceResult.fail(AuthErrorCodeEnum.USER_NOT_EXISTS, "user not found.");
        }

        // 校验用户密码是否正确
        List<UserDTO> matchUsers = new ArrayList<>();
        for (UserDTO existUser : existUsers) {
            // 校验用户名与密码是否符合
            if (PwdUtil.checkPassword(password, existUser.getPassword(), existUser.getSalt())) {
                matchUsers.add(existUser);
            }
        }

        // 判断激活中的用户数量
        if (CollectionUtils.isEmpty(matchUsers)) {
            // 用户数量等于0: 登录失败
            captchaUtil.setLoginCaptcha(response, username, ipAddress);
            return ServiceResult.fail(AuthErrorCodeEnum.USER_NAME_NOT_EXISTS, "user_name or password not true.");
        } else if (matchUsers.size() == 1) {
            // 用户数量等于1: 登录
            captchaUtil.clearLoginCaptcha(response, username, ipAddress);
            UserDTO userDTO = matchUsers.get(0);
            // 查询并校验公司
            OrganizationDTO organizationDTO = organizationService.selectByIdAndStatus(userDTO.getOrgId(), OrganizationStatusEnum.ACTIVATED.getStatus()).getData();
            if (organizationDTO == null) {
                return ServiceResult.fail(AuthErrorCodeEnum.ORG_NOT_EXISTS, "org not find");
            }
            return this.mobileFastLoginResult(userDTO, organizationDTO, ipAddress);
        } else {
            // 用户数量大于1: 查询用户的所属公司进行相应的处理
            return this.phoneOrEmailMultLoginResult(response, existUsers, username, sessionId, ipAddress);
        }
    }

    @Override
    public ServiceResult<UserLoginDTO> orgAndUsernameLogin(HttpServletResponse response, OrgLoginDTO orgLoginDTO) {
        // 校验参数
        if (StringUtils.isBlank(orgLoginDTO.getUsername())) {
            return ServiceResult.fail(ValidationErrorCodeEnum.USER_NAME_EMPTY, "user_name is empty.");
        }
        if (StringUtils.isBlank(orgLoginDTO.getPassword())) {
            return ServiceResult.fail(ValidationErrorCodeEnum.PWD_EMPTY, "password is empty.");
        }

        // 存放需要的用户和公司参数
        Map<String, Object> paramMap = new HashMap<>();
        // 判断是否是官网登录
        if (Boolean.FALSE.equals(orgLoginDTO.getIsHomePage()) || singleOrgProperties != null) {
            // 非官网登录
            ServiceResult<UserLoginDTO> nonOfficialWebsiteLoginRt = this.nonOfficialWebsiteLogin(orgLoginDTO, paramMap);
            if (!nonOfficialWebsiteLoginRt.isSuccess()) {
                return nonOfficialWebsiteLoginRt;
            }
        } else {
            // 官网登录
            ServiceResult<UserLoginDTO> officialWebsiteLoginRt = this.officialWebsiteLogin(response, orgLoginDTO, paramMap);
            if (!officialWebsiteLoginRt.isSuccess()) {
                return officialWebsiteLoginRt;
            }
        }

        // 获取需要的用户和公司参数
        UserDTO existUser = (UserDTO) paramMap.get(UserDTO.class.getSimpleName());
        OrganizationDTO organizationDTO = (OrganizationDTO) paramMap.get(OrganizationDTO.class.getSimpleName());

        // 用户和公司都正常,进行登录操作
        // 判断密码是否加密
        if (orgLoginDTO.getEncrypt() != null && orgLoginDTO.getEncrypt()) {
            // 密码解密
            ServiceResult<String> decryptPasswordResult = userPrivateKeyBusinessService.decryptPassword(organizationDTO.getOrgId(), orgLoginDTO.getPassword());
            if (!decryptPasswordResult.isSuccess()) {
                return ServiceResult.fail(decryptPasswordResult.getErrorData(), decryptPasswordResult.getMsg());
            }
            orgLoginDTO.setPassword(decryptPasswordResult.getData());
        }
        // 登录校验密码-30分钟内允许尝试5次登录
        ServiceResult<Void> checkPasswordWithLimitRt = this.checkPasswordWithLimit(existUser, organizationDTO.getCode(), orgLoginDTO.getPassword());
        if (!checkPasswordWithLimitRt.isSuccess()) {
            if (String.valueOf(AuthErrorCodeEnum.USER_NAME_NOT_EXISTS.getCode()).equals(checkPasswordWithLimitRt.getErrorCode())) {
                captchaUtil.setLoginCaptcha(response, orgLoginDTO.getUsername(), orgLoginDTO.getIpAddress());
            }
            return ServiceResult.fail(null, checkPasswordWithLimitRt.getMsg(), checkPasswordWithLimitRt.getErrorCode());
        }

        // 判断是否需要多因素认证登录
        /*ServiceResult<JudgeNeedMfaLoginDTO> judgeNeedMfaLoginRt = userMfaSettingService.judgeNeedMfaLogin(existUser.getOrgId(), existUser);
        if (!judgeNeedMfaLoginRt.isSuccess()) {
            return ServiceResult.fail(userLoginConvert.needMfa2UserLoginDTO(judgeNeedMfaLoginRt.getData()),
                    judgeNeedMfaLoginRt.getMsg(), judgeNeedMfaLoginRt.getErrorCode());
        }*/

        // 不管绑定与否，只要登录了，就先拿用户信息.退出登录，清空用户信息
        if (StringUtils.isNotBlank(orgLoginDTO.getSessionId())) {
            WxCacheDTO wxCacheDTO = userWechatGrpcService.getWxSessionInfo(orgLoginDTO.getSessionId());
            wxCacheDTO.setEmail(existUser.getEmail());
            wxCacheDTO.setPhone(existUser.getPhone());
            wxCacheDTO.setName(existUser.getName());
            userWechatGrpcService.cacheWxSessionInfo(orgLoginDTO.getSessionId(), wxCacheDTO);
        }

        // 校验成功,用户登录
        captchaUtil.clearLoginCaptcha(response, orgLoginDTO.getUsername(), orgLoginDTO.getIpAddress());
        return this.orgAndUsernameLoginSuccess(existUser, organizationDTO, orgLoginDTO.getIpAddress());
    }

    @Override
    public ServiceResult<UserLoginDTO> fastRegister(OrgRegisterDTO orgRegisterDTO) {
        // 校验参数
        if (orgRegisterDTO.getHasAcceptPolicy() == null || !orgRegisterDTO.getHasAcceptPolicy()) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "need accept");
        }
        if (StringUtils.isBlank(orgRegisterDTO.getSessionId())) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "session_id lost");
        }
        if (StringUtils.isBlank(orgRegisterDTO.getIndustryCode())) {
            return ServiceResult.fail(ResourceErrorCodeEnum.NOT_FOUND, "industry not found");
        }
        // 校验邮箱
        if (StringUtils.isBlank(orgRegisterDTO.getEmail())) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "email is empty.");
        }
        orgRegisterDTO.setEmail(StringUtils.strip(orgRegisterDTO.getEmail()));
        if (Boolean.FALSE.equals(EmailUtil.validateEmail(orgRegisterDTO.getEmail()))) {
            return ServiceResult.fail(ValidationErrorCodeEnum.EMAIL_ERROR, "邮箱有误,请重新输入");
        }
        // 校验场景
        if (StringUtils.isBlank(orgRegisterDTO.getScenarioId())) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "scenario_id is empty.");
        }
        ServiceResult<UserScenarioDTO> scenarioResult = userScenarioService.getUserScenarioById(orgRegisterDTO.getScenarioId());
        if (Objects.isNull(scenarioResult.getData())) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "scenario_id is empty.");
        }

        // 获取并校验缓存中的session信息
        FastCreateUserDTO fastCreateUserDTO = businessCacheUtil.getFastCreateUser(orgRegisterDTO.getSessionId());
        if (fastCreateUserDTO == null || StringUtils.isBlank(fastCreateUserDTO.getPhone())) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, String.format("session %s not found", orgRegisterDTO.getSessionId()));
        }
        UserRegisterDTO userRegisterDTO = userLoginConvert.fastCreateUserDto2Register(fastCreateUserDTO);

        // 小程序快速注册
        // 通过行业编码和ip获取行业信息和行业对应的地区的销售电话
        IndustryDTO industryDTO = this.getIndustryAndSalesEmails(orgRegisterDTO.getIpAddress(), orgRegisterDTO.getIndustryCode());
        if (industryDTO == null || Objects.isNull(industryDTO.getCode())) {
            return ServiceResult.fail(ResourceErrorCodeEnum.NOT_FOUND, "industry not found");
        }

        // 设置参数
        orgRegisterDTO.setName(StringUtils.isNotBlank(orgRegisterDTO.getName()) ? orgRegisterDTO.getName() : StringUtils.EMPTY);
        orgRegisterDTO.setUserName(StringUtils.isNotBlank(orgRegisterDTO.getUserName()) ? orgRegisterDTO.getUserName() : userRegisterDTO.getPhone());
        orgRegisterDTO.setPhone(StringUtils.isNotBlank(orgRegisterDTO.getPhone()) ? orgRegisterDTO.getPhone() : userRegisterDTO.getPhone());
        orgRegisterDTO.setOrgName(StringUtils.isNotBlank(orgRegisterDTO.getOrgName()) ? orgRegisterDTO.getOrgName() : StringUtils.EMPTY);
        orgRegisterDTO.setEmail(StringUtils.isNotBlank(orgRegisterDTO.getEmail()) ? orgRegisterDTO.getEmail() : StringUtils.EMPTY);

        // 注册公司-设置公司和用户
        ServiceResult<JSONObject> registerOrgRt = userOrgBusinessService.registerOrg(orgRegisterDTO, userRegisterDTO, industryDTO);
        if (!registerOrgRt.isSuccess()) {
            ServiceResult<UserLoginDTO> failRt = ServiceResult.fail(registerOrgRt.getMsg());
            failRt.setMsg(registerOrgRt.getMsg());
            return failRt;
        }
        // 获取公司和用户
        UserDTO userDTO = registerOrgRt.getData().getObject(UserDTO.class.getName(), UserDTO.class);
        OrganizationDTO organizationDTO = registerOrgRt.getData().getObject(OrganizationDTO.class.getName(), OrganizationDTO.class);
        // 获取类型为基础试用版的套餐信息
        PackageDTO freeTrialPackageDTO = registerOrgRt.getData().getObject(PackageDTO.class.getName(), PackageDTO.class);

        // 创建用户登录日志
        userBusinessService.updateUserLastLoginTime(userDTO, orgRegisterDTO.getIpAddress());

        // 发送免费公司创建邮件/短信给用户
        userWxService.notifyFreeOrgCreated(organizationDTO.getCode(), userDTO.getUserName(), organizationDTO.getName(),
                null, organizationDTO.getPackageExpireTime(), userDTO.getPhone(), organizationDTO.getOrgId());

        String source = StringUtils.EMPTY;
        if (StringUtils.isNotBlank(orgRegisterDTO.getUtmSource()) && StringUtils.isNotBlank(orgRegisterDTO.getPlan())) {
            source = String.format("?utm_source=%s&plan=%s", orgRegisterDTO.getUtmSource(), orgRegisterDTO.getPlan());
        }
        String content = StringUtils.EMPTY;
        if (StringUtils.isNotBlank(fastCreateUserDTO.getWxMiniOpenid())) {
            // 小程序 注册时即发送邮件给销售
            source = StringUtils.isBlank(source) ? "小程序注册" : source;
            content = EmailContentEnum.WECHAT_MINI_PROGRAM.getDesc();
        }
        // 统一都给销售发送邮件
        NotifySalesDTO notifySalesDTO = new NotifySalesDTO(userDTO, organizationDTO.getOrgId(), organizationDTO.getName(),
                content, organizationDTO.getNotifySalesFlag(), source, industryDTO.getName(), scenarioResult.getData());
        userUtil.notifySales(notifySalesDTO);

        // 删除session_id缓存
        businessCacheUtil.delFastCreateUser(orgRegisterDTO.getSessionId());

        // 设置埋点信息
        this.createBookingTrack(orgRegisterDTO, userDTO, organizationDTO);

        // 异步回调百度回传转化数据
        baiduOcpcService.sendConvertData(orgRegisterDTO.getNewType(), orgRegisterDTO.getLogidUrl());

        // 设置token
        AuthDTO authDTO = AuthDTO.builder()
                .userId(userDTO.getUserId())
                .realName(userDTO.getName())
                .avatar(userDTO.getAvatar())
                .superRole(NumberUtils.INTEGER_ONE)
                .orgId(organizationDTO.getOrgId())
                .orgCode(organizationDTO.getCode())
                .packageExpireTime(UserJwtTokenUtil.formatDate(organizationDTO.getPackageExpireTime()))
                .build();
        // 设置返回值
        UserLoginDTO userLoginDTO = this.buildLoginResult(authDTO, userDTO, organizationDTO, freeTrialPackageDTO.getName());
        return ServiceResult.success(userLoginDTO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ServiceResult<String> bishareToken(String shareId, String password) {
        // 参数校验
        // apps_mysql.auth.handlers.AuthTokenViewBiShareHandler.post
        if (StringUtils.isBlank(shareId)) {
            return ServiceResult.fail(ValidationErrorCodeEnum.BI_SHARE_INFO_ERROR, "share_id is empty.");
        }

        // 根据share_id查询bi_share信息
        BiShareDTO shareInfo = userBaseBiGrpcService.getShareById(shareId);
        // share状态位判定
        if (Objects.isNull(shareInfo) || !shareInfo.getStatus().equals(ShareStatus.SHARE_STATUS_ENABLE_VALUE)) {
            return ServiceResult.fail(ValidationErrorCodeEnum.BI_SHARE_INFO_ERROR, "share is not available");
        }

        // 如果share开启了密码校验，则开始校验share信息的密码
        Integer requestPwdFlag = shareInfo.getRequestPwdFlag();
        String requestPwd = shareInfo.getRequestPwd();
        if (ShareRequestPwdFlag.SHARE_REQUEST_PWD_FLAG_ENABLE_VALUE == requestPwdFlag
                && !password.equals(DigestUtil.md5Hex(requestPwd))) {
            // 密码校验失败
            return ServiceResult.fail(ValidationErrorCodeEnum.BI_SHARE_INFO_ERROR, "访问密码错误");
        }

        // 开始处理share信息业务逻辑
        String orgId = "";
        // 提取出project对象，为了后续流程使用
        ProjectDTO project = null;
        // 提取userId，为了后续是否需要创建临时用户使用
        String userId = "";
        // 根据share不同的类型，做不同的处理
        switch (shareInfo.getShareType()) {
            // 情况一：数据看板类型
            // 创建该share对应的project的creator的org下的user
            case ShareShareType.SHARE_SHARE_TYPE_PROP_VALUE:
                // 查询project
                project = userBaseQdesGrpcService.getProjectById(shareInfo.getProjectId());
                if (Objects.nonNull(project)) {
                    orgId = project.getCompanyId();
                    break;
                }
                return ServiceResult.fail(ResourceErrorCodeEnum.NOT_FOUND, "未查询project信息");
            // 情况二：仪表盘类型
            case ShareShareType.SHARE_SHARE_TYPE_DASHBOARD_VALUE:
                userId = shareInfo.getShareUserId();
                // 查询dashboard
                DashboardDTO dashboard = userBaseBiGrpcService.getDashboardById(shareInfo.getDashboardId());
                if (Objects.nonNull(dashboard)) {
                    orgId = dashboard.getOrgId();
                    break;
                }
                return ServiceResult.fail(ResourceErrorCodeEnum.NOT_FOUND, "未查询dashboard信息");
            default:
                return ServiceResult.fail(ValidationErrorCodeEnum.BI_SHARE_INFO_ERROR, "share_type is wrong.");
        }

        // 根据orgId获取org信息
        ServiceResult<OrganizationDTO> organizationResult = organizationService.selectByPrimaryKey(orgId);
        if (!organizationResult.isSuccess() || Objects.isNull(organizationResult.getData())) {
            return ServiceResult.fail(ResourceErrorCodeEnum.NOT_FOUND, "未查询到公司信息");
        }

        // 校验公司套餐信息
        OrganizationDTO organization = organizationResult.getData();
        boolean checkPrgPack = checkOrgPack(organization);
        if (!checkPrgPack) {
            return ServiceResult.fail(AuthErrorCodeEnum.ORG_EXPIRE, "org authorization is expired");
        }

        // 创建jwt信息
        AuthDTO.AuthDTOBuilder builder = AuthDTO.builder();

        // 判断是否需要创建临时用户
        if (StringUtils.isEmpty(userId)) {
            // 根据公司信息，获取或创建临时用户
            ServiceResult<UserDTO> userResult = userService.getOrCreateBiShareTempUser(orgId);
            if (!userResult.isSuccess() || Objects.isNull(userResult.getData())) {
                return ServiceResult.fail(ResourceErrorCodeEnum.NOT_FOUND, "获取或创建临时用户失败");
            }
            UserDTO userInfo = userResult.getData();
            // 检查project_entry
            if (Objects.nonNull(project)) {
                checkProjectEntry(project, userInfo.getUserId());
            }
            builder.userId(userInfo.getUserId());
            builder.realNameForBiShare(userInfo.getUserName());
        } else {
            builder.userId(userId);
        }

        builder.orgId(organization.getOrgId());
        builder.orgCode(organization.getCode());
        builder.shareId(shareId);
        builder.timeout("" + 48 * 60 * 60);
        builder.requestPwd(Boolean.TRUE.equals(BooleanStatusEnum.toBoolean(requestPwdFlag)) ? requestPwd : "");
        builder.biShare(true);
        String token = userJwtTokenUtil.generateToken(builder.build());
        return ServiceResult.success(token);
    }

    @Override
    public ServiceResult<WxLoginDTO> getTokenByUser(UserDTO userDTO) {
        // 参数校验
        if (Objects.isNull(userDTO)) {
            return ServiceResult.fail(AuthErrorCodeEnum.USER_NOT_EXISTS, "user not exist.");
        }

        // 校验公司信息
        ServiceResult<OrganizationDTO> organizationResult = organizationService.selectByIdAndStatus(userDTO.getOrgId(), OrganizationStatusEnum.ACTIVATED.getStatus());
        if (!organizationResult.isSuccess() || Objects.isNull(organizationResult.getData())) {
            return ServiceResult.fail(AuthErrorCodeEnum.ORG_NOT_EXISTS, "org not find");
        }
        OrganizationDTO organizationDTO = organizationResult.getData();

        // 判断公司套餐是否到期
        if (Objects.nonNull(organizationDTO.getPackageExpireTime()) && organizationDTO.getPackageExpireTime().before(new Date())) {
            return ServiceResult.fail(AuthErrorCodeEnum.ORG_EXPIRE, DateUtil.convertDteToString(organizationDTO.getPackageExpireTime(), DateUtil.YYYY_MM_DD));
        }

        // 设置AutoDTO build信息
        AuthDTO.AuthDTOBuilder builder = AuthDTO.builder();
        // 设置公司信息
        builder.orgId(organizationDTO.getOrgId());
        builder.orgCode(organizationDTO.getCode());
        builder.packageExpireTime(DateUtil.convertDteToString(organizationDTO.getPackageExpireTime(), DateUtil.YYYY_MM_DD));
        // 设置用户信息
        builder.userId(userDTO.getUserId());
        builder.realName(userDTO.getName());
        builder.avatar(userDTO.getAvatar());

        // 设置用户是否是系统管理员和高级管理员
        builder.superRole(userBusinessService.checkSuper(userDTO.getUserId(), userDTO.getOrgId()).getData());
        builder.senior(userBusinessService.checkSenior(userDTO.getUserId(), userDTO.getOrgId()).getData());

        // 生成jwt
        String token = userJwtTokenUtil.generateToken(builder.build());
        String refreshToken = userJwtTokenUtil.generateRefreshToken(organizationDTO.getCode(), userDTO.getUserId());

        // 设置登陆展示信息
        OrgDisplayDTO orgDisplayDTO = new OrgDisplayDTO();
        orgDisplayDTO.setCode(organizationDTO.getCode());
        orgDisplayDTO.setName(organizationDTO.getName());
        orgDisplayDTO.setOrgId(organizationDTO.getOrgId());
        orgDisplayDTO.setUsername(userDTO.getUserName());
        orgDisplayDTO.setDisplayUserName(userBusinessService.getDisplayUserName(userDTO));

        // 设置登陆展示信息
        OrgCodeDTO orgCodeDTO = new OrgCodeDTO();
        orgCodeDTO.setOrgCode(organizationDTO.getCode());
        orgCodeDTO.setOrgName(organizationDTO.getName());
        orgCodeDTO.setOrgId(organizationDTO.getOrgId());
        orgCodeDTO.setUserName(userDTO.getUserName());
        orgCodeDTO.setDisplayName(userBusinessService.getDisplayUserName(userDTO));

        // 更新最后登录时间
        updateUserLastLoginTime(userDTO.getUserId());

        // 获取package信息
        PackageDTO packageDTO = packageGrpcService.getById(organizationDTO.getPackageId());
        String packageName = packageDTO.getName();

        // 获取埋点数据
        GrowingioDTO growingio = growingIOService.getUserProperty(userDTO, organizationDTO, packageName);

        // 返回UserLoginVO
        WxLoginDTO wxLoginDTO = new WxLoginDTO();
        wxLoginDTO.setOrgCode(organizationDTO.getCode());
        wxLoginDTO.setToken(token);
        wxLoginDTO.setRefreshToken(refreshToken);
        wxLoginDTO.setGrowingio(growingio);
        wxLoginDTO.setOrgCodeDTO(Collections.singletonList(orgCodeDTO));
        return ServiceResult.success(wxLoginDTO);
    }

    @Override
    public ServiceResult<WenJuanLoginResultDTO> wenJuanLogin(String code, String host, String protocol) {
        // python引用：apps_mysql.user.v3.wjw_login.LoginByWenJuanHandler.get
        // 定义返回对象，默认重定向回登录页
        WenJuanLoginResultDTO wenJuanLoginResultDTO = new WenJuanLoginResultDTO();
        wenJuanLoginResultDTO.setRedirectUrl("/login");
        String domain = ".bestcem.com";

        if (StringUtils.isBlank(code)) {
            log.info("code:{}", code);
            // 未传code，直接重定向到login页
            return ServiceResult.success(wenJuanLoginResultDTO);
        }

        // 通过尝试加锁判断是否有别的进程在操作
        String lockKey = MessageFormat.format(UserCacheKeyConstant.WJW_LOCK_KEY, code);
        boolean lockFlag = redisService.tryLock(lockKey, TimeUnit.SECONDS, 1, 5);
        if (!lockFlag) {
            log.info("wjw login 尝试获取锁失败，code:{}", code);
            return ServiceResult.success(wenJuanLoginResultDTO);
        }

        // 调用问卷网，根据code查询问卷用户信息
        ServiceResult<WenJuanUserInfoDTO> userInfo = wenJuanService.getUserInfo(code);
        if (!userInfo.isSuccess() || Objects.isNull(userInfo.getData()) || StringUtils.isBlank(userInfo.getData().getMobile())) {
            log.warn("调用问卷网，根据code查询问卷用户信息，code:{}，result:{}", code, JSON.toJSONString(userInfo));
            return ServiceResult.success(wenJuanLoginResultDTO);
        }
        WenJuanUserInfoDTO wenJuanUserInfoDTO = userInfo.getData();

        // 根据手机号查询激活状态的用户列表
        String phone = wenJuanUserInfoDTO.getMobile();
        UserDTO userDTO = new UserDTO();
        userDTO.setPhone(phone);
        userDTO.setStatus(UserStatusEnum.ACTIVATED.getStatus());
        ServiceResult<List<UserDTO>> userDtos = userService.selectList(userDTO);

        // 如果这个手机号有多个user多个公司则让用户选择
        if (userDtos.isSuccess() && CollectionUtils.isNotEmpty(userDtos.getData()) && userDtos.getData().size() > 1) {
            // 根据用户手机号查询企业列表信息
            List<String> orgIds = userDtos.getData().stream().map(UserDTO::getOrgId).collect(Collectors.toList());
            // 组装用户对应的orgId的map关系
            Map<String, UserDTO> orgUserMap = userDtos.getData().stream().collect(Collectors.toMap(UserDTO::getOrgId, u -> u, (u1, u2) -> u1));
            ServiceResult<List<OrganizationDTO>> orgDtos = organizationService.selectByIdsAndStatus(orgIds, null);
            // 企业列表数大于1，相关操作
            if (orgDtos.isSuccess() && CollectionUtils.isNotEmpty(orgDtos.getData()) && orgDtos.getData().size() > 1) {
                List<WjwOrganizationInfoDTO> wjwOrganizationInfos = orgDtos.getData().stream()
                        .filter(o -> orgUserMap.containsKey(o.getOrgId()))
                        .map(o -> {
                            UserDTO user = orgUserMap.get(o.getOrgId());
                            WjwOrganizationInfoDTO dto = new WjwOrganizationInfoDTO();
                            dto.setOrgCode(o.getCode());
                            dto.setOrgName(o.getName());
                            dto.setOrgId(o.getOrgId());
                            dto.setDisplayName(userBusinessService.getDisplayUserName(user));
                            dto.setUserName(user.getUserName());
                            return dto;
                        }).collect(Collectors.toList());

                // 调用快速登录接口使用的session_id
                UserLoginSessionDTO sessionDTO = new UserLoginSessionDTO();
                sessionDTO.setPhone(phone);
                String sessionId = businessCacheUtil.cacheFastLoginUser(sessionDTO);

                // 获取公司列表的session_Id
                String orgSessionId = businessCacheUtil.genSessionId();
                redisService.set(userCacheUtil.buildKey(UserCacheKeyConstant.WENJUAN_LOGIN_ORG_LIST, orgSessionId), JSON.toJSONString(wjwOrganizationInfos));
                // 设置cookie
                List<Cookie> cookieList = new ArrayList<>();
                cookieList.add(setCookie("wjw_login_bestcem_login_session_id", sessionId, domain, 24 * 60 * 60));
                cookieList.add(setCookie("wjw_login_bestcem_org_uuid", orgSessionId, domain, 24 * 60 * 60));
                wenJuanLoginResultDTO.setCookieList(cookieList);
                wenJuanLoginResultDTO.setRedirectUrl("/connect-login");
                return ServiceResult.success(wenJuanLoginResultDTO);
            }
        }

        // 根据手机号或unionId查询用户信息
        ServiceResult<UserDTO> userInfoDTO = userService.selectByPhoneOrUnionId(phone, wenJuanUserInfoDTO.getUnionId(),
                UserStatusEnum.ACTIVATED.getStatus());
        UserDTO user = userInfoDTO.getData();
        if (!userInfoDTO.isSuccess() || Objects.isNull(user)) {
            List<Cookie> cookieList = new ArrayList<>();
            cookieList.add(setCookie("wjw_has_login", "true", domain, 24 * 60 * 60));
            cookieList.add(setCookie("connect_login_username", phone, domain, 24 * 60 * 60));

            // 缓存微信user信息
            UserRegisterDTO cache = new UserRegisterDTO();
            cache.setPhone(wenJuanUserInfoDTO.getMobile());
            cache.setHeadImgUrl(wenJuanUserInfoDTO.getHeadImgUrl());
            cache.setWxNickname(wenJuanUserInfoDTO.getNickname());
            cache.setWxUnionid(wenJuanUserInfoDTO.getUnionId());
            cache.setWxOffiOpenid("");
            String sessionId = businessCacheUtil.cacheWxUserInfo(cache);
            cookieList.add(setCookie("wenjuan_session_id", sessionId, domain, 2 * 60 * 60));
            wenJuanLoginResultDTO.setCookieList(cookieList);
            wenJuanLoginResultDTO.setRedirectUrl("/register");
            return ServiceResult.success(wenJuanLoginResultDTO);
        }

        // 根据orgId判断是否需要mfa登录
        String orgId = user.getOrgId();
        ServiceResult<JudgeNeedMfaLoginDTO> judgeNeedMfaLogin = userMfaSettingService.judgeNeedMfaLogin(orgId, user);
        if (!judgeNeedMfaLogin.isSuccess()) {
            ServiceResult<UserMfaSettingDTO> mfaSettingByOrgId = userMfaSettingService.getMfaSettingByOrgId(orgId);
            List<Cookie> cookieList = new ArrayList<>();
            cookieList.add(setCookie("user_id", user.getUserId(), domain, 24 * 60 * 60));
            cookieList.add(setCookie("mobile", user.getPhone(), domain, 24 * 60 * 60));
            cookieList.add(setCookie("email", user.getEmail(), domain, 24 * 60 * 60));
            UserMfaSettingDTO data = mfaSettingByOrgId.getData();
            if (Objects.nonNull(data)) {
                cookieList.add(setCookie("auth_method", "" + data.getAuthMethod(), domain, 24 * 60 * 60));
                cookieList.add(setCookie("allow_skip", "" + BooleanStatusEnum.toBoolean(data.getAllowSkip()), domain, 24 * 60 * 60));
            }
            wenJuanLoginResultDTO.setCookieList(cookieList);
            wenJuanLoginResultDTO.setRedirectUrl("/login?needmfa=true&source=wj");
            return ServiceResult.success(wenJuanLoginResultDTO);
        }

        // 获取用户的token信息
        ServiceResult<UserLoginDTO> tokenByUser = getTokenByUserDto(user);
        if (!tokenByUser.isSuccess() || Objects.isNull(tokenByUser.getData())) {
            return ServiceResult.fail(tokenByUser.getErrorData(), tokenByUser.getErrorData().getMsg());
        }
        UserLoginDTO tokenInfo = tokenByUser.getData();
        String orgCode = tokenInfo.getOrgCode();
        String token = tokenInfo.getToken();
        List<Cookie> cookieList = new ArrayList<>();
        cookieList.add(setCookie("bestcem_org_code", orgCode, domain, 24 * 60 * 60));
        cookieList.add(setCookie("preOrgCode", orgCode, domain, 24 * 60 * 60));
        cookieList.add(setCookie("bestcem_token", token, domain, 24 * 60 * 60));
        cookieList.add(setCookie("bestcem_refresh_token", token, domain, 24 * 60 * 60));
        wenJuanLoginResultDTO.setCookieList(cookieList);

        host = host.replace("www.", "");
        wenJuanLoginResultDTO.setRedirectUrl(protocol + "://" + orgCode + "." + host);
        return ServiceResult.success(wenJuanLoginResultDTO);
    }

    @Override
    public ServiceResult<List<WjwOrganizationInfoDTO>> getWjwCacheOrgInfos(String orgUuid) {
        if (StringUtils.isBlank(orgUuid)) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "orgUuid not be null.");
        }

        String cacheKey = userCacheUtil.buildKey(UserCacheKeyConstant.WENJUAN_LOGIN_ORG_LIST, orgUuid);
        Boolean hashKey = redisService.hasKey(cacheKey);
        if (!Boolean.TRUE.equals(hashKey)) {
            return ServiceResult.success(new ArrayList<>());
        }

        Object json = redisService.get(cacheKey);
        List<WjwOrganizationInfoDTO> wjwOrganizationInfoDTOS = JSON.parseArray(json.toString(), WjwOrganizationInfoDTO.class);
        if (CollectionUtils.isEmpty(wjwOrganizationInfoDTOS)) {
            return ServiceResult.success(new ArrayList<>());
        }
        return ServiceResult.success(wjwOrganizationInfoDTOS);
    }

    @Override
    public ServiceResult<String> checkRegisterPhone(String phone, Integer edition) {
        // 获取测试注册白名单
        if (!settingProperties.getTestWhitePhoneList().contains(phone)) {
            // 如果手机号不在测试注册白名单手机号内,判断手机号是否已经注册
            UserDTO userCondition = new UserDTO();
            userCondition.setPhone(phone);
            userCondition.setEdition(edition);
            ServiceResult<List<UserDTO>> existUserRt = userService.listByCondition(userCondition);
            if (CollectionUtils.isNotEmpty(existUserRt.getData())) {
                String msg = edition == null ? "该手机号已在系统中, 请前往登录" : null;
                return ServiceResult.fail(ValidationErrorCodeEnum.PHONE_EXISTS_ERROR, msg);
            }
        }

        return ServiceResult.success();
    }

    @Override
    public ServiceResult<Void> completeOrgInfo(CompleteOrgInfoDTO completeOrgInfo) {
        // python引用：apps_mysql/user/v3/handlers.py:232
        // 参数校验
        if (StringUtils.isBlank(completeOrgInfo.getUserId())) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "userId 不存在");
        }
        if (StringUtils.isBlank(completeOrgInfo.getOrgId())) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "orgId 不存在");
        }

        // 添加广告来源字段
        String utmSource = completeOrgInfo.getUtmSource();
        String plan = completeOrgInfo.getPlan();
        String source = "";
        if (StringUtils.isNotBlank(utmSource) && StringUtils.isNotBlank(plan)) {
            source = "?utm_source=" + utmSource + "&plan=" + plan;
        }

        // 组装更新organization参数
        OrganizationDTO orgDTO = new OrganizationDTO();
        if (StringUtils.isNotBlank(completeOrgInfo.getOrgName())) {
            orgDTO.setName(completeOrgInfo.getOrgName());
        }
        // 获取行业信息
        String industryName = "";
        if (StringUtils.isNotBlank(completeOrgInfo.getIndustryCode())) {
            IndustryDTO industryDTO = industryGrpcService.getByCode(completeOrgInfo.getIndustryCode());
            if (Objects.nonNull(industryDTO)) {
                orgDTO.setIndustryId(industryDTO.getIndustryId());
                industryName = industryDTO.getName();
            }
        }
        // 设置已经完善信息
        orgDTO.setCompleteFlag(BooleanStatusEnum.YES.getStatus());

        // 获取用户，更新用户
        ServiceResult<UserDTO> userInfoServiceResult = userService.selectById(completeOrgInfo.getUserId());
        if (!userInfoServiceResult.isSuccess() || Objects.isNull(userInfoServiceResult.getData())) {
            return ServiceResult.fail(ResourceErrorCodeEnum.NOT_FOUND, "未找到对应的用户信息");
        }
        UserDTO userInfo = userInfoServiceResult.getData();

        UserDTO userDTO = new UserDTO();
        userDTO.setUserId(userInfo.getUserId());
        userDTO.setName(completeOrgInfo.getName());
        userDTO.setUserName(completeOrgInfo.getUserName());
        if (Objects.nonNull(completeOrgInfo.getHasAcceptPolicy())) {
            userDTO.setAcceptFlag(BooleanStatusEnum.toInteger(completeOrgInfo.getHasAcceptPolicy()));
        }
        // 更新用户
        userService.updateById(userDTO);

        // 没有密码，重置密码
        if (StringUtils.isNotBlank(completeOrgInfo.getPassWord())) {
            if (StringUtils.isNotBlank(userInfo.getPassword())) {
                return ServiceResult.fail(ResourceErrorCodeEnum.FOUND, "密码已存在");
            }
            // 重置密码
            resetPassword(userInfo.getUserId(), completeOrgInfo.getPassWord());
        }

        // 更新公司信息
        orgDTO.setOrgId(completeOrgInfo.getOrgId());
        organizationService.updateByPrimaryKeySelective(orgDTO);

        // 查询公司信息
        ServiceResult<OrganizationDTO> orgServiceResult = organizationService.selectByPrimaryKey(orgDTO.getOrgId());
        if (Objects.nonNull(orgServiceResult.getData()) && StringUtils.isNotBlank(userDTO.getPhone())) {
            String content = Boolean.TRUE.equals(completeOrgInfo.getOnPhone())
                    ? EmailContentEnum.MOBILE_H5_REGISTER.getDesc() : "";
            OrganizationDTO orgInfo = orgServiceResult.getData();
            // 通知销售
            NotifySalesDTO notifySalesDTO = new NotifySalesDTO(userInfo, orgInfo.getOrgId(), orgInfo.getName(), content,
                    orgInfo.getNotifySalesFlag(), source, industryName, null);
            userUtil.notifySales(notifySalesDTO);
        }

        // 更新埋点数据信息
        UserBookingTrackDTO userBookingTrackDTO = new UserBookingTrackDTO();
        userBookingTrackDTO.setOrgCode(completeOrgInfo.getOrgCode());
        userBookingTrackDTO.setName(completeOrgInfo.getName());
        userBookingTrackDTO.setPhone(userInfo.getPhone());
        userBookingTrackDTO.setIndustry(industryName);
        userBookingTrackDTO.setOrgName(completeOrgInfo.getOrgName());
        userBookingTrackService.updateSelectiveByOrgCode(userBookingTrackDTO);

        return ServiceResult.success();
    }

    @Override
    public ServiceResult<Void> jumpCompleteOrgInfo(String orgId, String userId) {
        // 参数校验
        if (StringUtils.isBlank(userId)) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "userId不存在");
        }
        if (StringUtils.isBlank(orgId)) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "orgId不存在");
        }
        ServiceResult<UserDTO> userAccount = userService.getUserByPrimaryKey(userId);
        UserDTO userInfo = userAccount.getData();
        if (Objects.nonNull(userInfo) && StringUtils.isNotBlank(userInfo.getPhone())
                && !BooleanStatusEnum.YES.getStatus().equals(userInfo.getInnerFlag())) {
            ServiceResult<OrganizationDTO> organizationResult = organizationService.selectByPrimaryKey(orgId);
            OrganizationDTO organizationInfo = organizationResult.getData();
            if (Objects.isNull(organizationInfo)) {
                return ServiceResult.fail(ResourceErrorCodeEnum.NOT_FOUND, "未查询到租户信息");
            }
            // 无需通知的情况
            if (BooleanStatusEnum.YES.getStatus().equals(organizationInfo.getNotifySalesFlag())) {
                return ServiceResult.success();
            }
            // 通知销售
            NotifySalesDTO notifySalesDTO = new NotifySalesDTO(userInfo, orgId, organizationInfo.getName(), "",
                    organizationInfo.getNotifySalesFlag(), "", "", null);
            userUtil.notifySales(notifySalesDTO);
        }
        return ServiceResult.success();
    }

    @Override
    public ServiceResult<ResetPasswordResultDTO> resetPasswordByToken(ResetPasswordParamDTO resetPasswordParam) {
        // python引用：apps_mysql/user/v3/handlers.py:405
        // 参数校验
        if (StringUtils.isEmpty(resetPasswordParam.getUserIds())) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "userId不能为空");
        }
        ResetPasswordResultDTO result = new ResetPasswordResultDTO();
        List<String> userIdList = Lists.newArrayList(resetPasswordParam.getUserIds().split("_"));
        // 重置密码,用户id是否有效校验
        for (String uid : userIdList) {
            if (!ObjectId.isValid(uid)) {
                return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "userId错误");
            }
        }

        // 校验token信息
        String cacheToken = userCacheUtil.get(UserCacheKeyConstant.RESET_TOKEN_KEY, resetPasswordParam.getUserIds());
        if (StringUtils.isBlank(cacheToken) || !resetPasswordParam.getToken().equals(cacheToken)) {
            return ServiceResult.fail(UserErrorEnum.INVALID_ACTIVATE_TOKEN, "invalid token or expire token");
        }
        // 重置用户密码
        for (String userId : userIdList) {
            resetPassword(userId, resetPasswordParam.getPassword());
        }

        // 根据用户id列表查询启用中的用户列表
        ServiceResult<List<UserDTO>> userServiceResult = userService.selectByIds(userIdList);
        List<UserDTO> userDtos = userServiceResult.getData().stream()
                .filter(u -> u.getStatus().equals(UserStatusEnum.ACTIVATED.getStatus())).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(userDtos)) {
            return ServiceResult.fail(AuthErrorCodeEnum.USER_NAME_NOT_EXISTS, "未查询到用户信息");
        }

        if (userDtos.size() > 1) {
            // 获取企业id列表
            List<String> orgIds = userDtos.stream().map(UserDTO::getOrgId).collect(Collectors.toList());
            // 批量查询激活状态的企业列表信息
            ServiceResult<List<OrganizationDTO>> organizations = organizationService.selectByIdsAndStatus(orgIds,
                    OrganizationStatusEnum.ACTIVATED.getStatus());
            if (!organizations.isSuccess() || CollectionUtils.isEmpty(organizations.getData())) {
                return ServiceResult.fail(AuthErrorCodeEnum.ORG_NOT_EXISTS, "未查询到对应的公司信息");
            }
            // 遍历企业和用户列表，删除用户缓存，更新用户token
            for (OrganizationDTO org : organizations.getData()) {
                for (UserDTO userDto : userDtos) {
                    businessCacheUtil.delUserLocked(org.getCode(), userDto);
                    userCacheUtil.delete(UserCacheKeyConstant.USER_FAILED_LOGIN_COUNT, org.getCode(), userDto.getUserName(),
                            userDto.getPhone(), userDto.getEmail());
                    // 更新用户的token版本号
                    userJwtTokenUtil.updateTokenVersion(userDto.getUserId());
                }
            }
            List<OrganizationCodeInfoDTO> orgCodeList = organizations.getData().stream()
                    .map(o -> new OrganizationCodeInfoDTO(o.getCode(), o.getName(), o.getOrgId())).collect(Collectors.toList());
            result.setMultipleOrg(true);
            result.setOrgCodeList(orgCodeList);
            return ServiceResult.success(result);
        }

        // 获取第一个用户信息
        UserDTO userInfo = userDtos.get(0);
        // 根据第一个用户获取对应的公司信息
        ServiceResult<OrganizationDTO> organizationDTOServiceResult = organizationService.selectByPrimaryKey(userInfo.getOrgId());
        if (!organizationDTOServiceResult.isSuccess() || Objects.isNull(organizationDTOServiceResult.getData())) {
            return ServiceResult.fail(AuthErrorCodeEnum.ORG_NOT_EXISTS, "未查询到对应的公司信息");
        }
        OrganizationDTO orgInfo = organizationDTOServiceResult.getData();

        // 开始构建token对象信息
        AuthDTO.AuthDTOBuilder builder = AuthDTO.builder();
        builder.orgCode(orgInfo.getCode());
        builder.orgId(orgInfo.getOrgId());
        builder.userId(userInfo.getUserId());
        builder.realName(userInfo.getName());
        builder.avatar(userInfo.getAvatar());
        // 设置用户是否是系统管理员和高级管理员
        builder.superRole(userBusinessService.checkSuper(userInfo.getUserId(), orgInfo.getOrgId()).getData());
        builder.senior(userBusinessService.checkSenior(userInfo.getUserId(), orgInfo.getOrgId()).getData());
        // 更新用户的token版本号
        userJwtTokenUtil.updateTokenVersion(userInfo.getUserId());

        // 根据jwt信息生成token
        String tokenResult = userJwtTokenUtil.generateToken(builder.build());
        String refreshTokenResult = userJwtTokenUtil.generateRefreshToken(orgInfo.getCode(), userInfo.getUserId());

        // 删除登录失败的锁和次数
        businessCacheUtil.delUserLocked(orgInfo.getCode(), userInfo);
        userCacheUtil.delete(UserCacheKeyConstant.USER_FAILED_LOGIN_COUNT, orgInfo.getCode(), userInfo.getUserName(),
                userInfo.getPhone(), userInfo.getEmail());

        // 邮件中重置密码链接置为失效
        userCacheUtil.delete(UserCacheKeyConstant.RESET_TOKEN_KEY, resetPasswordParam.getUserIds());

        result.setMultipleOrg(false);
        result.setOrgCode(orgInfo.getCode());
        result.setOrgId(orgInfo.getOrgId());
        result.setToken(tokenResult);
        result.setRefreshToken(refreshTokenResult);
        return ServiceResult.success(result);
    }

    @Override
    public ServiceResult<ResetPasswordResultDTO> resetPasswordByOrgCode(ResetPasswordParamDTO resetPasswordParam) {
        // 参数校验
        if (StringUtils.isEmpty(resetPasswordParam.getOrgCode())) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "orgCode不能为空");
        }
        if (StringUtils.isEmpty(resetPasswordParam.getUserIds())) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "userId不能为空");
        }
        ResetPasswordResultDTO result = new ResetPasswordResultDTO();
        List<String> userIdList = Lists.newArrayList(resetPasswordParam.getUserIds().split("_"));
        AuthDTO.AuthDTOBuilder builder = AuthDTO.builder();

        // 根据orgCode查询对应的公司信息
        ServiceResult<OrganizationDTO> organizationResult = organizationService.selectByOrgCode(resetPasswordParam.getOrgCode());
        if (!organizationResult.isSuccess() || Objects.isNull(organizationResult.getData())
                || !organizationResult.getData().getStatus().equals(OrganizationStatusEnum.ACTIVATED.getStatus())) {
            return ServiceResult.fail(AuthErrorCodeEnum.ORG_NOT_EXISTS, "未查询到对应的公司信息");
        }
        OrganizationDTO orgInfo = organizationResult.getData();
        // 判断公司的套餐的有效期
        if (Objects.nonNull(orgInfo.getPackageExpireTime())) {
            Date packageExpireTime = orgInfo.getPackageExpireTime();
            if (packageExpireTime.before(new Date())) {
                return ServiceResult.fail(AuthErrorCodeEnum.ORG_EXPIRE, "公司有效期已过期");
            }
            builder.packageExpireTime(DateUtil.convertDteToString(packageExpireTime, DateUtil.YYYY_MM_DD));
        }
        // 批量查询用户信息
        ServiceResult<List<UserDTO>> userServiceResult = userService.selectByIds(userIdList);
        if (!userServiceResult.isSuccess() || CollectionUtils.isEmpty(userServiceResult.getData())) {
            return ServiceResult.fail(AuthErrorCodeEnum.USER_NAME_NOT_EXISTS, "未查询到用户信息");
        }
        // 从用户列表中获取一个当前公司下的启用中的用户
        UserDTO userInfo = userServiceResult.getData().stream().filter(u -> u.getOrgId().equals(orgInfo.getOrgId())).findFirst().orElse(null);
        if (Objects.isNull(userInfo) || !userInfo.getStatus().equals(UserStatusEnum.ACTIVATED.getStatus())) {
            return ServiceResult.fail(AuthErrorCodeEnum.USER_NAME_NOT_EXISTS, "未查询到用户信息");
        }

        // 构造jwt用户信息
        builder.orgCode(orgInfo.getCode());
        builder.orgId(orgInfo.getOrgId());
        builder.userId(userInfo.getUserId());
        builder.realName(userInfo.getName());
        builder.avatar(userInfo.getAvatar());
        // 设置用户是否是系统管理员和高级管理员
        builder.superRole(userBusinessService.checkSuper(userInfo.getUserId(), orgInfo.getOrgId()).getData());
        builder.senior(userBusinessService.checkSenior(userInfo.getUserId(), orgInfo.getOrgId()).getData());
        // 更新用户的token版本号
        userJwtTokenUtil.updateTokenVersion(userInfo.getUserId());

        // 根据jwt信息生成token
        String tokenResult = userJwtTokenUtil.generateToken(builder.build());
        String refreshTokenResult = userJwtTokenUtil.generateRefreshToken(orgInfo.getCode(), userInfo.getUserId());

        // 删除登录失败的锁和次数
        businessCacheUtil.delUserLocked(orgInfo.getCode(), userInfo);
        userCacheUtil.delete(UserCacheKeyConstant.USER_FAILED_LOGIN_COUNT, orgInfo.getCode(), userInfo.getUserName(),
                userInfo.getPhone(), userInfo.getEmail());

        // 邮件中重置密码链接置为失效
        userCacheUtil.delete(UserCacheKeyConstant.RESET_TOKEN_KEY, resetPasswordParam.getUserIds());

        result.setMultipleOrg(false);
        result.setOrgCode(orgInfo.getCode());
        result.setOrgId(orgInfo.getOrgId());
        result.setToken(tokenResult);
        result.setRefreshToken(refreshTokenResult);
        return ServiceResult.success(result);
    }

    @Override
    public ServiceResult<String> resetPassword(String userId, String token, String password) {
        // python引用：apps_mysql.user.util.utils.reset_password_by_find
        // 参数校验
        if (StringUtils.isBlank(userId)) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "userId not be null");
        }
        // 校验token信息
        String cacheToken = userCacheUtil.get(UserCacheKeyConstant.RESET_PASSWORD_TOKEN_KEY, userId);
        if (StringUtils.isBlank(cacheToken) || !token.equals(cacheToken)) {
            return ServiceResult.fail(UserErrorEnum.INVALID_ACTIVATE_TOKEN, "invalid token or expire token");
        }

        ServiceResult<Boolean> resetPasswordResult = resetPassword(userId, password);
        if (!resetPasswordResult.isSuccess()) {
            return ServiceResult.fail(resetPasswordResult.getErrorData(), resetPasswordResult.getMsg());
        }
        // 更新token版本号
        userJwtTokenUtil.updateTokenVersion(userId);
        return ServiceResult.success(userId);
    }

    @Override
    public void updateUserLastLoginTime(String userId) {
        UserDTO updateUser = new UserDTO();
        updateUser.setUserId(userId);
        updateUser.setLastLoginTime(new Date());
        userService.updateById(updateUser);
    }

    @Override
    public ServiceResult<Void> delUserLock(String userName, String orgCode) {
        // python引用：apps_mysql.user.handlers.DelUserLockHandler.post
        // 参数校验
        if (StringUtils.isBlank(userName) || StringUtils.isBlank(orgCode)) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "user_name or org_code null");
        }

        // 根据orgCode获取org信息
        ServiceResult<OrganizationDTO> organizationDTO = organizationService.selectByOrgCode(orgCode);
        if (!organizationDTO.isSuccess() || Objects.isNull(organizationDTO.getData())) {
            return ServiceResult.fail(AuthErrorCodeEnum.ORG_NOT_EXISTS, "org not found");
        }
        OrganizationDTO organization = organizationDTO.getData();
        // 根据userName和orgId查询用户信息
        UserDTO param = new UserDTO();
        param.setUserName(userName);
        param.setOrgId(organization.getOrgId());
        ServiceResult<List<UserDTO>> listServiceResult = userService.selectList(param);
        if (CollectionUtils.isNotEmpty(listServiceResult.getData())) {
            UserDTO userDTO = listServiceResult.getData().get(0);
            // 删除对应的缓存
            businessCacheUtil.delUserLocked(orgCode, userDTO);
        }

        return ServiceResult.success();
    }

    @Override
    public ServiceResult<Void> sendResetPwdEmail(String captchaKey, String email, String captchaCode, String orgCode, String uri,
                                                 String serverName, String protocol) {
        // 参数校验
        if (StringUtils.isBlank(uri) || !uri.startsWith("/")) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_FORMAT, "uri must starts with /");
        }
        // 校验图形验证码
        ServiceResult<Void> checkImgCaptcha = captchaUtil.checkImgCaptcha(captchaKey, captchaCode);
        if (!checkImgCaptcha.isSuccess()) {
            return checkImgCaptcha;
        }

        //防刷检测
        ServiceResult<Boolean> checkFrequency = userService.checkFrequency("reset_pwd_" + email, 60, 1);
        if (!checkFrequency.isSuccess()) {
            return ServiceResult.fail(ResourceErrorCodeEnum.TOO_BUSY, "60s内不可以重复发送");
        }

        // 发送重置密码邮件
        ServiceResult<Void> sendResetPwdEmailResult = userUtil.sendResetPwdEmail(orgCode, serverName, protocol, email, uri);
        if (!sendResetPwdEmailResult.isSuccess()) {
            return ServiceResult.fail(sendResetPwdEmailResult.getErrorData(), sendResetPwdEmailResult.getMsg());
        }
        return ServiceResult.success();
    }


    /**
     * 飞书对接获取token
     *
     * @param userId 用户id
     * @return user login info
     * @author XiaoYunSong <ys.xiao@idiaoyan.com>
     * @date 2022/6/24 13:51
     */
    @Override
    public ServiceResult<UserLoginDTO> feishuGetToken(String userId) {
        ServiceResult<UserDTO> userDTOServiceResult = userService.selectById(userId);
        if (!userDTOServiceResult.isSuccess()) {
            return ServiceResult.fail(userDTOServiceResult.getErrorData(), userDTOServiceResult.getMsg());
        }
        UserDTO user = userDTOServiceResult.getData();
        if (Objects.isNull(user)) {
            return ServiceResult.fail(ResourceErrorCodeEnum.NOT_FOUND, "用户不存在");
        }
        return getTokenByUserDto(user);
    }


    /**
     * bi分享假token-创建临时用户的projectentry
     * apps_mysql.auth.utils.check_project_entry
     *
     * @param project 项目信息
     * @param userId  用户id
     * @author cuiqiongyu <qiongyu.cui@idiaoyan.cn>
     * @date 2022/03/29 18:35
     */
    private void checkProjectEntry(ProjectDTO project, String userId) {
        if (Objects.isNull(project) || StringUtils.isBlank(userId)) {
            return;
        }
        String projectId = project.getId();
        // 根据projectId和userId查询
        List<ProjectEntryDTO> projectEntryDTOS = userBaseQdesGrpcService.listProjectEntry(projectId, userId);
        if (CollectionUtils.isEmpty(projectEntryDTOS)) {
            String creator = project.getCreator();
            List<ProjectEntryDTO> projectEntries = userBaseQdesGrpcService.listProjectEntry(projectId, creator);
            if (CollectionUtils.isNotEmpty(projectEntries)) {
                ProjectEntryDTO projectEntryDTO = projectEntries.get(0);
                projectEntryDTO.setUserId(userId);
                // 创建projectEntry
                userBaseQdesGrpcService.createProjectEntry(projectEntryDTO);
            }
        }
    }

    /**
     * 校验公司套餐并处理token信息
     *
     * @param orgInfo 公司查询结果
     * @param builder token对象构造器
     * @return com.bestcem.xm.util.ServiceResult<com.bestcem.xm.user.service.dto.user.AuthDTO.AuthDTOBuilder>
     * @author cuiqiongyu <qiongyu.cui@idiaoyan.cn>
     * @date 2022/05/27 16:58
     */
    private ServiceResult<AuthDTO.AuthDTOBuilder> checkPackAndHandlerToken(ServiceResult<OrganizationDTO> orgInfo,
                                                                           AuthDTO.AuthDTOBuilder builder) {
        // 根据公司id查询公司信息
        if (!orgInfo.isSuccess() || Objects.isNull(orgInfo.getData())) {
            return ServiceResult.fail(ResourceErrorCodeEnum.NOT_FOUND, "organization be disabled.");
        }
        OrganizationDTO organization = orgInfo.getData();
        // 校验套餐是否过期
        if (Objects.nonNull(organization.getPackageExpireTime())) {
            if (organization.getPackageExpireTime().before(new Date())) {
                return ServiceResult.fail(AuthErrorCodeEnum.ORG_EXPIRE,
                        DateUtil.convertDteToString(organization.getPackageExpireTime(), DateUtil.YYYY_MM_DD));
            }
            builder.packageExpireTime(DateUtil.convertDteToString(organization.getPackageExpireTime(), DateUtil.YYYY_MM_DD));
        }
        builder.orgId(organization.getOrgId());
        builder.orgCode(organization.getCode());
        builder.orgName(organization.getName());
        return ServiceResult.success(builder);
    }

    /**
     * 校验公司套餐信息
     *
     * @param organizationDTO 公司信息
     * @return boolean
     * @author cuiqiongyu <qiongyu.cui@idiaoyan.cn>
     * @date 2022/03/29 17:22
     */
    private boolean checkOrgPack(OrganizationDTO organizationDTO) {
        if (Objects.isNull(organizationDTO)) {
            return false;
        }

        // 查询公司套餐信息
        PackageDTO packageDTO = packageGrpcService.getById(organizationDTO.getPackageId());
        if (Objects.isNull(packageDTO)) {
            return false;
        }

        Date createTime = organizationDTO.getCreateTime();
        Date packageExpireTime = organizationDTO.getPackageExpireTime();
        Date freeExpireTime = organizationDTO.getFreeExpireTime();
        Date now = new Date();
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DATE, -14);
        Date fourteenBefore = calendar.getTime();

        if (PackageEditionEnum.PACK_EDITION_FREE.getType().equals(packageDTO.getEdition())) {
            if (Objects.nonNull(createTime) && createTime.before(fourteenBefore)) {
                return false;
            } else {
                return Objects.isNull(freeExpireTime) || freeExpireTime.after(now);
            }
        } else if (Objects.equals(PackageEditionEnum.PACK_EDITION_ENTERPRISE.getType(), packageDTO.getEdition())) {
            return Objects.isNull(packageExpireTime) || packageExpireTime.after(now);
        }
        return false;
    }

    /**
     * 设置cookie
     *
     * @param name   name
     * @param value  value
     * @param domain domain
     * @param expire expire 单位：s
     * @return javax.servlet.http.Cookie
     * @author cuiqiongyu <qiongyu.cui@idiaoyan.cn>
     * @date 2022/03/21 13:42
     */
    private Cookie setCookie(String name, String value, String domain, int expire) {
        Cookie cookie = new Cookie(name, value);
        if (StringUtils.isBlank(domain)) {
            domain = ".bestcem.com";
        }
        cookie.setDomain(domain);
        cookie.setMaxAge(expire);
        cookie.setPath("/");
        return cookie;
    }

    /**
     * 根据用户信息获取用户的登录信息
     *
     * @param userDTO 用户的信息
     * @return com.bestcem.xm.util.ServiceResult<com.bestcem.xm.user.service.dto.user.UserLoginDTO>
     * @author cuiqiongyu <qiongyu.cui@idiaoyan.cn>
     * @date 2022/03/21 14:00
     */
    private ServiceResult<UserLoginDTO> getTokenByUserDto(UserDTO userDTO) {
        // 查询并校验公司信息
        if (!ObjectId.isValid(userDTO.getOrgId())) {
            return ServiceResult.fail(AuthErrorCodeEnum.ORG_NOT_EXISTS, "org not find");
        }
        OrganizationDTO organizationDTO = organizationService.selectByIdAndStatus(userDTO.getOrgId(),
                OrganizationStatusEnum.ACTIVATED.getStatus()).getData();
        if (Objects.isNull(organizationDTO)) {
            return ServiceResult.fail(AuthErrorCodeEnum.ORG_NOT_EXISTS, "org not find");
        }
        Date now = new Date();
        AuthDTO.AuthDTOBuilder authBuilder = AuthDTO.builder();
        if (Objects.nonNull(organizationDTO.getPackageExpireTime())) {
            if (now.after(organizationDTO.getPackageExpireTime())) {
                return ServiceResult.fail(AuthErrorCodeEnum.ORG_EXPIRE, "公司有效期已过期");
            }
            authBuilder.packageExpireTime(UserJwtTokenUtil.formatDate(organizationDTO.getPackageExpireTime()));
        }
        authBuilder.orgCode(organizationDTO.getCode());
        authBuilder.orgId(organizationDTO.getOrgId());
        authBuilder.userId(userDTO.getUserId());
        authBuilder.realName(userDTO.getName());
        authBuilder.avatar(userDTO.getAvatar());

        // 设置用户是否是系统管理员和高级管理员
        authBuilder.superRole(userBusinessService.checkSuper(userDTO.getUserId(), userDTO.getOrgId()).getData());
        authBuilder.senior(userBusinessService.checkSenior(userDTO.getUserId(), userDTO.getOrgId()).getData());

        // 根据租户的packageId查询套餐信息
        PackageDTO packageDTO = packageGrpcService.getById(organizationDTO.getPackageId());
        if (packageDTO == null) {
            log.error("用户登录, 公司套餐未查询到. organizationDTO = [{}]", organizationDTO.toString());
        }

        // 设置用户登录返回值
        UserLoginDTO userLoginDTO = this.buildLoginResult(authBuilder.build(), userDTO, organizationDTO, packageDTO != null ? packageDTO.getName() : StringUtils.EMPTY);
        List<OrgDisplayDTO> orgDisplayDTOS = new ArrayList<>();
        OrgDisplayDTO orgDisplayDTO = new OrgDisplayDTO();
        orgDisplayDTO.setOrgId(organizationDTO.getOrgId());
        orgDisplayDTO.setCode(organizationDTO.getCode());
        orgDisplayDTO.setName(organizationDTO.getName());
        orgDisplayDTO.setUsername(userDTO.getUserName());
        orgDisplayDTO.setDisplayUserName(userBusinessService.getDisplayUserName(userDTO));
        orgDisplayDTOS.add(orgDisplayDTO);
        userLoginDTO.setOrgDisplayList(orgDisplayDTOS);

        // 更新用户的最近登录时间 + 创建用户登录日志
        userBusinessService.updateUserLastLoginTime(userDTO, "");

        return ServiceResult.success(userLoginDTO);
    }

    /**
     * 创建需要注册返回值
     *
     * @param phone 手机号
     * @return 注册返回值
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/4 14:30
     */
    private ServiceResult<UserLoginDTO> mobileFastRegisterResult(String phone) {
        // 设置缓存
        FastCreateUserDTO fastCreateUserDTO = new FastCreateUserDTO();
        fastCreateUserDTO.setPhone(phone);
        String sessionId = businessCacheUtil.cacheFastCreateUser(fastCreateUserDTO);

        // 设置返回信息
        UserLoginDTO registerInfo = new UserLoginDTO();
        registerInfo.setSessionId(sessionId);
        registerInfo.setRegisterFlag(NumberUtils.INTEGER_ONE);
        return ServiceResult.success(registerInfo);
    }

    /**
     * 用户登录
     *
     * @param userDTO         用户dto
     * @param organizationDTO 公司
     * @param ipAddress       ip地址
     * @return
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/7 14:00
     */
    private ServiceResult<UserLoginDTO> mobileFastLoginResult(UserDTO userDTO, OrganizationDTO organizationDTO, String ipAddress) {
        // 校验试用套餐到期禁止登陆
        Date now = UserDateUtil.getDateWithoutTime(DataBaseUtil.getDate());
        if (organizationDTO.getTrialExpireTime() != null) {
            Date trialExpireTime = UserDateUtil.getDateWithoutTime(organizationDTO.getTrialExpireTime());
            if (now.after(trialExpireTime) && UserEnum.USER_YES.getFlag().equals(organizationDTO.getBanLoginTrialExpiration())) {
                return ServiceResult.fail(AuthErrorCodeEnum.BAN_LOGIN_TRIAL_EXPIRYDT, "试用套餐到期禁止登陆");
            }
        }
        // 校验用户是否被锁定登录
        ServiceResult<Void> userLockedRt = businessCacheUtil.checkUserLocked(organizationDTO.getOrgId(), userDTO);
        if (!userLockedRt.isSuccess()) {
            return ServiceResult.fail(null, userLockedRt.getMsg(), userLockedRt.getErrorCode());
        }
        // 判断是否需要mfa登录
        ServiceResult<JudgeNeedMfaLoginDTO> judgeNeedMfaLoginResult = userMfaSettingService.judgeNeedMfaLogin(userDTO.getOrgId(), userDTO);
        if (!judgeNeedMfaLoginResult.isSuccess()) {
            return ServiceResult.fail(userLoginConvert.needMfa2UserLoginDTO(judgeNeedMfaLoginResult.getData()),
                    judgeNeedMfaLoginResult.getMsg(), judgeNeedMfaLoginResult.getErrorCode());
        }

        // 判断公司套餐是否已经到期
        if (organizationDTO.getPackageExpireTime() != null) {
            Date packageExpireTime = UserDateUtil.getDateWithoutTime(organizationDTO.getPackageExpireTime());
            if (now.after(packageExpireTime)) {
                return ServiceResult.fail(AuthErrorCodeEnum.ORG_EXPIRE, UserJwtTokenUtil.formatDate(organizationDTO.getPackageExpireTime()));
            }
        }

        // 设置登录token
        AuthDTO authDTO = AuthDTO.builder()
                .packageExpireTime(UserJwtTokenUtil.formatDate(organizationDTO.getPackageExpireTime()))
                .orgCode(organizationDTO.getCode())
                .orgId(organizationDTO.getOrgId())
                .userId(userDTO.getUserId())
                .realName(userDTO.getName())
                .avatar(userDTO.getAvatar())
                .build();
        // 设置用户是否是系统管理员和高级管理员
        authDTO.setSuperRole(userBusinessService.checkSuper(userDTO.getUserId(), userDTO.getOrgId()).getData());
        authDTO.setSenior(userBusinessService.checkSenior(userDTO.getUserId(), userDTO.getOrgId()).getData());

        // 获取类型为基础试用版的套餐信息
        PackageDTO packageDTO = packageGrpcService.getById(organizationDTO.getPackageId());
        if (packageDTO == null) {
            log.error("用户登录, 公司套餐未查询到. organizationDTO = [{}]", organizationDTO.toString());
        }

        // 设置用户登录返回值
        UserLoginDTO userLoginDTO = this.buildLoginResult(authDTO, userDTO, organizationDTO, packageDTO != null ? packageDTO.getName() : StringUtils.EMPTY);
        // 设置用户登录返回值的公司展示信息
        List<OrgDisplayDTO> orgDisplayList = new ArrayList<>();
        orgDisplayList.add(buildOrgDisplay(organizationDTO, userDTO));
        userLoginDTO.setOrgDisplayList(orgDisplayList);

        // 创建用户登录日志
        userDTO.setUpdateTime(DataBaseUtil.getDate());
        userBusinessService.updateUserLastLoginTime(userDTO, ipAddress);

        return ServiceResult.success(userLoginDTO);
    }

    /**
     * 多用户登录处理
     *
     * @param existUsers
     * @param ipAddress
     * @return ServiceResult<UserLoginDTO>
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/6/17 21:53
     */
    private ServiceResult<UserLoginDTO> mobileFastMultLoginResult(List<UserDTO> existUsers, String ipAddress) {
        // 查询并设置用户的所属公司
        List<OrgDisplayDTO> orgDisplayList = new ArrayList<>();
        // 可登录的用户和公司
        UserDTO user = null;
        OrganizationDTO organization = null;
        for (UserDTO existUser : existUsers) {
            OrganizationDTO organizationDTO = organizationService.selectByIdAndStatus(existUser.getOrgId(), null).getData();
            // 查询到公司设置可登录的公司和用户
            if (organizationDTO != null) {
                orgDisplayList.add(buildOrgDisplay(organizationDTO, existUser));
                user = existUser;
                organization = organizationDTO;
            }
        }

        // 判断用户的所属公司数量
        if (CollectionUtils.isEmpty(orgDisplayList)) {
            // 用户的所属公司不存在
            return ServiceResult.fail(AuthErrorCodeEnum.ORG_NOT_EXISTS, "org not find");
        } else if (orgDisplayList.size() == 1) {
            // 仅找到一个企业直接登录
            // 用户数量等于1: 登录
            assert organization != null;
            return this.mobileFastLoginResult(user, organization, ipAddress);
        } else {
            // 多个企业 用户选择企业登录
            UserLoginDTO userLoginDTO = new UserLoginDTO();
            userLoginDTO.setMultiOrg(true);
            userLoginDTO.setOrgDisplayList(orgDisplayList);

            // 存储用户信息 保存登录seeionId缓存
            UserLoginSessionDTO userLoginSessionDTO = new UserLoginSessionDTO();
            assert user != null;
            userLoginSessionDTO.setPhone(user.getPhone());
            userLoginDTO.setSessionId(businessCacheUtil.cacheFastLoginUser(userLoginSessionDTO));
            return ServiceResult.success(userLoginDTO);
        }
    }

    /**
     * 多用户登录处理
     *
     * @param response
     * @param existUsers
     * @param username
     * @param sessionId
     * @param ipAddress
     * @return ServiceResult<UserLoginDTO>
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/6/18 1:43
     */
    private ServiceResult<UserLoginDTO> phoneOrEmailMultLoginResult(HttpServletResponse response, List<UserDTO> existUsers, String username, String sessionId, String ipAddress) {
        // 查询并设置用户的所属公司
        List<OrgDisplayDTO> orgDisplayList = new ArrayList<>();
        // 可登录的用户和公司
        UserDTO user = null;
        OrganizationDTO organization = null;
        for (UserDTO existUser : existUsers) {
            OrganizationDTO organizationDTO = organizationService.selectByIdAndStatus(existUser.getOrgId(), null).getData();
            // 查询到公司设置可登录的公司和用户
            if (organizationDTO != null) {
                orgDisplayList.add(buildOrgDisplay(organizationDTO, existUser));
                user = existUser;
                organization = organizationDTO;
            }
        }

        // 判断用户的所属公司数量
        if (CollectionUtils.isEmpty(orgDisplayList)) {
            // 用户的所属公司不存在
            captchaUtil.setLoginCaptcha(response, username, ipAddress);
            return ServiceResult.fail(AuthErrorCodeEnum.USER_NAME_NOT_EXISTS, "user_name or password not true.");
        } else if (orgDisplayList.size() == 1) {
            // 仅找到一个企业直接登录
            captchaUtil.clearLoginCaptcha(response, username, ipAddress);
            assert organization != null;
            ServiceResult<UserLoginDTO> result = this.mobileFastLoginResult(user, organization, ipAddress);
            // 不管绑定与否，只要登录了，就先拿用户信息.退出登录，清空用户信息
            if (StringUtils.isNotBlank(sessionId)) {
                WxCacheDTO wxCacheDTO = userWechatGrpcService.getWxSessionInfo(sessionId);
                assert user != null;
                wxCacheDTO.setEmail(user.getEmail());
                wxCacheDTO.setPhone(user.getPhone());
                wxCacheDTO.setName(user.getName());
                userWechatGrpcService.cacheWxSessionInfo(sessionId, wxCacheDTO);
            }
            return result;
        } else {
            // 多个企业 用户选择企业登录
            captchaUtil.clearLoginCaptcha(response, username, ipAddress);
            // 设置返回值
            UserLoginDTO userLoginDTO = new UserLoginDTO();
            userLoginDTO.setMultiOrg(Boolean.TRUE);
            userLoginDTO.setOrgDisplayList(orgDisplayList);
            return ServiceResult.success(userLoginDTO);
        }
    }

    /**
     * 设置用户登录返回值的公司展示信息
     *
     * @param organizationDTO
     * @param userDTO
     * @return
     */
    private OrgDisplayDTO buildOrgDisplay(OrganizationDTO organizationDTO, UserDTO userDTO) {
        OrgDisplayDTO orgDisplayDTO = new OrgDisplayDTO();
        orgDisplayDTO.setOrgId(organizationDTO.getOrgId());
        orgDisplayDTO.setCode(organizationDTO.getCode());
        orgDisplayDTO.setName(organizationDTO.getName());
        orgDisplayDTO.setUsername(userDTO.getUserName());
        orgDisplayDTO.setDisplayUserName(userBusinessService.getDisplayUserName(userDTO));

        return orgDisplayDTO;
    }

    /**
     * 官网登录
     *
     * @param response
     * @param orgLoginDTO
     * @param paramMap
     * @return ServiceResult<UserLoginDTO>
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/6/16 20:44
     */
    private ServiceResult<UserLoginDTO> officialWebsiteLogin(HttpServletResponse response, OrgLoginDTO orgLoginDTO, Map<String, Object> paramMap) {
        // 螺丝帽验证防止暴力登陆
        Long captchaCount = userCacheUtil.get(userCacheUtil.buildKey(UserCacheKeyConstant.LOGIN_CAPTCHA_COUNT, orgLoginDTO.getUsername(), orgLoginDTO.getIpAddress()), Long.class);
        captchaCount = captchaCount != null ? captchaCount
                : (orgLoginDTO.getLoginCaptchaCount() != null ? orgLoginDTO.getLoginCaptchaCount() : NumberUtils.LONG_ZERO);
        if (captchaCount > 2) {
            if (StringUtils.isBlank(orgLoginDTO.getLuotestResponse())) {
                return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, orgLoginDTO.getLuotestResponse());
            }
            // 螺丝帽点击验证
            ServiceResult<String> checkCaptchaRt = luosimaoService.checkCaptcha(orgLoginDTO.getLuotestResponse());
            if (!checkCaptchaRt.isSuccess()) {
                return ServiceResult.fail(ValidationErrorCodeEnum.CAPTCHA_ERROR, checkCaptchaRt.getMsg());
            }
        }

        // 根据手机号/邮箱/用户名查询用户
        List<UserDTO> existUsers = userBusinessService.findFreeUserByUsername(orgLoginDTO.getUsername()).getData();
        // 判断用户数量
        if (CollectionUtils.isEmpty(existUsers)) {
            // 用户数量为0
            captchaUtil.setLoginCaptcha(response, orgLoginDTO.getUsername(), orgLoginDTO.getIpAddress());
            return ServiceResult.fail(AuthErrorCodeEnum.USER_NOT_EXISTS, "user not found.");
        } else if (existUsers.size() > 1) {
            // 用户数量大于1, 选择用户登录
            return this.multiOrgLogin(response, existUsers, orgLoginDTO);
        }

        // 用户数量等于1, 登录
        UserDTO existUser = existUsers.get(0);
        // 查询并校验公司信息
        if (!ObjectId.isValid(existUser.getOrgId())) {
            captchaUtil.setLoginCaptcha(response, orgLoginDTO.getUsername(), orgLoginDTO.getIpAddress());
            return ServiceResult.fail(AuthErrorCodeEnum.ORG_NOT_EXISTS, "user_name or password not true.");
        }
        OrganizationDTO organizationDTO = organizationService.selectByIdAndStatus(existUser.getOrgId(), null).getData();
        if (organizationDTO == null) {
            captchaUtil.setLoginCaptcha(response, orgLoginDTO.getUsername(), orgLoginDTO.getIpAddress());
            return ServiceResult.fail(AuthErrorCodeEnum.ORG_NOT_EXISTS, "user_name or password not true.");
        }
        // 校验公司套餐是否已经到期
        if (organizationDTO.getPackageExpireTime() != null) {
            Date now = DataBaseUtil.getDate();
            Date packageExpireTime = UserDateUtil.getDateWithoutTime(organizationDTO.getPackageExpireTime());
            if (now.after(packageExpireTime)) {
                IndustryDTO industryDTO = industryGrpcService.getIndustryContactById(organizationDTO.getIndustryId());

                UserLoginDTO userLoginDTO = new UserLoginDTO();
                userLoginDTO.setSalesPhone(industryDTO.getContacts().get(0));
                captchaUtil.setLoginCaptcha(response, orgLoginDTO.getUsername(), orgLoginDTO.getIpAddress());
                return ServiceResult.fail(userLoginDTO, UserJwtTokenUtil.formatDate(organizationDTO.getPackageExpireTime()), String.valueOf(AuthErrorCodeEnum.ORG_EXPIRE.getCode()));
            }
        }

        paramMap.put(UserDTO.class.getSimpleName(), existUser);
        paramMap.put(OrganizationDTO.class.getSimpleName(), organizationDTO);
        return ServiceResult.success();
    }

    /**
     * 非官网登录
     *
     * @param orgLoginDTO
     * @param paramMap
     * @return ServiceResult<UserLoginDTO>
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/6/16 20:44
     */
    private ServiceResult<UserLoginDTO> nonOfficialWebsiteLogin(OrgLoginDTO orgLoginDTO, Map<String, Object> paramMap) {
        // 校验图形验证码(校验前置)
        if (Boolean.FALSE.equals(settingProperties.getIsXm())) {
            ServiceResult<Void> checkImgCaptchaResult = captchaUtil.checkImgCaptcha(orgLoginDTO.getCaptchaKey(), orgLoginDTO.getCaptchaCode());
            if (!checkImgCaptchaResult.isSuccess()) {
                return ServiceResult.fail(checkImgCaptchaResult.getErrorData(), checkImgCaptchaResult.getMsg());
            }
        }

        // 查询并校验公司
        ServiceResult<OrganizationDTO> organizationResult = organizationService.selectByCodeAndStatus(orgLoginDTO.getOrgCode(), OrganizationStatusEnum.ACTIVATED.getStatus());
        if (!organizationResult.isSuccess() || organizationResult.getData() == null) {
            return ServiceResult.fail(AuthErrorCodeEnum.ORG_NOT_EXISTS, "user_name or password not true.");
        }
        OrganizationDTO organizationDTO = organizationResult.getData();
        // 校验公司套餐是否已经到期
        Date now = UserDateUtil.getDateWithoutTime(DataBaseUtil.getDate());
        Date packageExpireTime = UserDateUtil.getDateWithoutTime(organizationDTO.getPackageExpireTime());
        if (packageExpireTime != null && now.after(packageExpireTime)) {
            return ServiceResult.fail(AuthErrorCodeEnum.ORG_EXPIRE, UserJwtTokenUtil.formatDate(organizationDTO.getPackageExpireTime()));
        }
        // 校验试用套餐到期禁止登陆
        if (organizationDTO.getTrialExpireTime() != null) {
            Date trialExpireTime = UserDateUtil.getDateWithoutTime(organizationDTO.getTrialExpireTime());
            if (now.after(trialExpireTime) && UserEnum.USER_YES.getFlag().equals(organizationDTO.getBanLoginTrialExpiration())) {
                return ServiceResult.fail(AuthErrorCodeEnum.BAN_LOGIN_TRIAL_EXPIRYDT, "试用套餐到期禁止登陆");
            }
        }

        // 根据手机号/邮箱/用户名查询用户
        List<UserDTO> existUsers = userBusinessService.listUserByPhoneOrEmail(orgLoginDTO.getUsername(), true, organizationDTO.getOrgId()).getData();
        if (CollectionUtils.isEmpty(existUsers)) {
            return ServiceResult.fail(AuthErrorCodeEnum.USER_NOT_EXISTS, "user not found.");
        }
        UserDTO existUser = existUsers.get(0);

        paramMap.put(UserDTO.class.getSimpleName(), existUser);
        paramMap.put(OrganizationDTO.class.getSimpleName(), organizationDTO);
        return ServiceResult.success();
    }

    /**
     * 用户登录成功: 根据公司+用户名+密码登录
     *
     * @param userDTO
     * @param organizationDTO
     * @param ipAddress
     * @return UserLoginDTO
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/6/16 17:57
     */
    private ServiceResult<UserLoginDTO> orgAndUsernameLoginSuccess(UserDTO userDTO, OrganizationDTO organizationDTO, String ipAddress) {
        AuthDTO authDTO = AuthDTO.builder()
                .packageExpireTime(UserJwtTokenUtil.formatDate(organizationDTO.getPackageExpireTime()))
                .orgCode(organizationDTO.getCode())
                .orgId(organizationDTO.getOrgId())
                .userId(userDTO.getUserId())
                .realName(userDTO.getName())
                .avatar(userDTO.getAvatar())
                .build();

        // 设置用户是否是系统管理员和高级管理员
        authDTO.setSuperRole(userBusinessService.checkSuper(userDTO.getUserId(), userDTO.getOrgId()).getData());
        authDTO.setSenior(userBusinessService.checkSenior(userDTO.getUserId(), userDTO.getOrgId()).getData());

        // 获取类型为基础试用版的套餐信息
        PackageDTO packageDTO = packageGrpcService.getById(organizationDTO.getPackageId());
        if (packageDTO == null) {
            log.error("用户登录, 公司套餐未查询到. organizationDTO = [{}]", organizationDTO.toString());
        }

        UserLoginDTO userLoginDTO = this.buildLoginResult(authDTO, userDTO, organizationDTO, packageDTO != null ? packageDTO.getName() : StringUtils.EMPTY);
        // 创建用户登录日志
        userDTO.setUpdateTime(DataBaseUtil.getDate());
        userBusinessService.updateUserLastLoginTime(userDTO, ipAddress);

        // 设置用户登录返回值
        return ServiceResult.success(userLoginDTO);
    }

    /**
     * 登录用户数量大于1: 查询用户的所属公司进行相应的处理
     *
     * @param response
     * @param userDTOList
     * @param orgLoginDTO
     * @return ServiceResult<UserLoginDTO>
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/6/16 10:28
     */
    private ServiceResult<UserLoginDTO> multiOrgLogin(HttpServletResponse response, List<UserDTO> userDTOList, OrgLoginDTO orgLoginDTO) {
        // 清除登录失败次数
        captchaUtil.clearLoginCaptcha(response, orgLoginDTO.getUsername(), orgLoginDTO.getIpAddress());

        // 查询用户对应的公司信息
        List<OrgDisplayDTO> orgDisplayList = new ArrayList<>();
        for (UserDTO userDTO : userDTOList) {
            OrganizationDTO organizationDTO = organizationService.selectByPrimaryKey(userDTO.getOrgId()).getData();
            if (organizationDTO == null) {
                // 设置登录失败次数
                captchaUtil.setLoginCaptcha(response, orgLoginDTO.getUsername(), orgLoginDTO.getIpAddress());
                return ServiceResult.fail(AuthErrorCodeEnum.USER_NAME_NOT_EXISTS, "user_name or password not true.");
            }
            OrgDisplayDTO orgDisplayDTO = new OrgDisplayDTO();
            orgDisplayDTO.setCode(organizationDTO.getCode());
            orgDisplayDTO.setName(organizationDTO.getName());
            orgDisplayDTO.setOrgId(organizationDTO.getOrgId());
            orgDisplayList.add(orgDisplayDTO);
        }

        // 设置返回信息
        UserLoginDTO userLoginDTO = new UserLoginDTO();
        userLoginDTO.setMultiOrg(Boolean.TRUE);
        userLoginDTO.setOrgDisplayList(orgDisplayList);
        return ServiceResult.success(userLoginDTO);
    }

    /**
     * 登录校验密码-30分钟内允许尝试5次登录
     *
     * @param userDTO  用户
     * @param orgCode  公司编码
     * @param password 密文密码
     * @return ServiceResult<Void>
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/6/15 16:24
     */
    private ServiceResult<Void> checkPasswordWithLimit(UserDTO userDTO, String orgCode, String password) {
        // 锁定账户缓存key
        ServiceResult<Void> userLockedRt = businessCacheUtil.checkUserLocked(orgCode, userDTO);
        if (!userLockedRt.isSuccess()) {
            return ServiceResult.fail(null, userLockedRt.getMsg(), userLockedRt.getErrorCode());
        }
        // 校验密码是否正确
        boolean checkPasswordSuccess = PwdUtil.checkPassword(password, userDTO.getPassword(), userDTO.getSalt());
        String failedCountCacheKey = userCacheUtil.buildKey(UserCacheKeyConstant.USER_FAILED_LOGIN_COUNT, orgCode, userDTO.getUserName(), userDTO.getPhone(), userDTO.getEmail());
        if (checkPasswordSuccess) {
            // 登录成功时清除失败计数
            userCacheUtil.delete(failedCountCacheKey);
            return ServiceResult.success();
        }

        // 登录失败次数缓存key
        Long failedCount = userCacheUtil.get(failedCountCacheKey, Long.class);
        if (failedCount == null) {
            // 从第一次错误开始计时，30分钟内允许尝试5次登录
            userCacheUtil.setEx(failedCountCacheKey, NumberUtils.INTEGER_ONE, 60L * 30);
        } else if (failedCount == 4) {
            // 第五次错误时清除失败次数，并锁定账号30分钟
            businessCacheUtil.cacheUserLocked(orgCode, userDTO);
            userCacheUtil.delete(failedCountCacheKey);
        } else {
            userCacheUtil.incrByKey(failedCountCacheKey);
        }

        return ServiceResult.fail(AuthErrorCodeEnum.USER_NAME_NOT_EXISTS, "user_name or password not true.");
    }

    /**
     * 通过用户名和密码查找用户
     * 传入org_code为查询单个用户，否则可能存在多个
     *
     * @param userName 登录用户名
     * @param password 登录密码
     * @param orgCode  公司编码
     * @return java.util.List<com.bestcem.xm.user.service.dto.user.UserDTO>
     * @author panyun <yun.pan@idiaoyan.com>
     * @date 2022/4/1 1:19
     */
    @Override
    public ServiceResult<List<UserDTO>> findUserByAuth(String userName, String password, String orgCode) {
        if (StringUtils.isBlank(userName) && StringUtils.isBlank(password)) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "userName和password不能同时为空");
        }

        List<UserDTO> userList = new ArrayList<>();
        if (StringUtils.isNotBlank(orgCode)) {
            OrganizationDTO organizationDTO = new OrganizationDTO();
            organizationDTO.setCode(orgCode);
            ServiceResult<List<OrganizationDTO>> organizationServiceResult = organizationService.selectBySelective(organizationDTO);
            if (organizationServiceResult.isSuccess() && !CollectionUtils.isEmpty(organizationServiceResult.getData())) {
                OrganizationDTO organization = organizationServiceResult.getData().get(0);

                UserDTO userDTO = new UserDTO();
                userDTO.setOrgId(organization.getOrgId())
                        .setStatus(UserStatusEnum.ACTIVATED.getStatus())
                        .setUserName(userName)
                        .setPhone(userName)
                        .setEmail(userName.toLowerCase());
                ServiceResult<List<UserDTO>> userByCondition = userService.listUserByOrCondition(userDTO);
                if (userByCondition.isSuccess() && !CollectionUtils.isEmpty(userByCondition.getData())) {
                    UserDTO dto = userByCondition.getData().get(0);
                    // 密码校验
                    if (PwdUtil.checkPassword(password, dto.getPassword(), dto.getSalt())) {
                        userList.add(dto);
                    }
                }
            }
        } else {
            UserDTO userDTO = new UserDTO();
            userDTO.setStatus(UserStatusEnum.ACTIVATED.getStatus())
                    .setPhone(userName)
                    .setEmail(userName.toLowerCase());
            ServiceResult<List<UserDTO>> userByCondition = userService.listUserByOrCondition(userDTO);
            if (userByCondition.isSuccess()) {
                for (UserDTO dto : userByCondition.getData()) {
                    if (PwdUtil.checkPassword(password, dto.getPassword(), dto.getSalt())) {
                        userList.add(dto);
                    }
                }
            }
        }

        if (CollectionUtils.isEmpty(userList)) {
            return ServiceResult.fail(AuthErrorCodeEnum.USER_NOT_EXISTS, "用户不存在");
        }
        return ServiceResult.success(userList);
    }

}
